HttpClient不能忽略的细节:关闭连接

本文介绍了使用Apache HttpClient时如何管理和释放HTTP连接以避免服务器连接被占满的问题。提供了四种方法,包括客户端主动关闭连接和服务器端自动关闭连接的方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

大部分人使用HttpClient都是使用类似下面的事例代码,包括Apache官方的例子也是如此:

[java]  view plain copy print ?
  1. HttpClient client = new HttpClient();  
  2. HttpMethod method = new GetMethod("http://www.apache.org");  
  3. try {  
  4.   client.executeMethod(method);  
  5.   byte[] responseBody = null;  
  6.     
  7.   responseBody = method.getResponseBody();  
  8.     
  9. catch (HttpException e) {  
  10.   // TODO Auto-generated catch block  
  11.   e.printStackTrace();  
  12. catch (IOException e) {  
  13.   // TODO Auto-generated catch block  
  14.   e.printStackTrace();  
  15. }finally{  
  16.   method.releaseConnection();  
  17.     
  18. }  

但最近我在使用HttpClient是发现一次循环发送大量请求到服务器会导致APACHE服务器的链接被占满,后续的请求便排队等待。 
在通过DEBUG后发现HttpClient在method.releaseConnection()后并没有把链接关闭,这个方法只是将链接返回给connection manager。如果使用HttpClient client = new HttpClient()实例化一个HttpClient connection manager默认实现是使用SimpleHttpConnectionManager。SimpleHttpConnectionManager有个构造函数如下 :

[java]  view plain copy print ?
  1. /** 
  2.  * The connection manager created with this constructor will try to keep the  
  3.  * connection open (alive) between consecutive requests if the alwaysClose  
  4.  * parameter is set to <tt>false</tt>. Otherwise the connection manager will  
  5.  * always close connections upon release. 
  6.  *  
  7.  * @param alwaysClose if set <tt>true</tt>, the connection manager will always 
  8.  *    close connections upon release. 
  9.  */  
  10. public SimpleHttpConnectionManager(boolean alwaysClose) {  
  11.     super();  
  12.     this.alwaysClose = alwaysClose;  
  13. }  

看方法注释我们就可以看到如果alwaysClose设为true在链接释放之后connection manager 就会关闭链。在我们HttpClient client = new HttpClient()这样实例化一个client时connection manager是这样被实例化的 

[java]  view plain copy print ?
  1. this.httpConnectionManager = new SimpleHttpConnectionManager();  

因此alwaysClose默认是false,connection是不会被主动关闭的,因此我们就有了一个客户端关闭链接的方法。 
方法一: 
把事例代码中的第一行实例化代码改为如下即可,在method.releaseConnection();之后connection manager会关闭connection 。 

[java]  view plain copy print ?
  1. HttpClient client = new HttpClient(new HttpClientParams(),new SimpleHttpConnectionManager(true) );  

方法二: 
实例化代码使用:HttpClient client = new HttpClient(); 
在method.releaseConnection();之后加上 

[java]  view plain copy print ?
  1. ((SimpleHttpConnectionManager)client.getHttpConnectionManager()).shutdown();  

shutdown源代码很简单,看了一目了然 
[java]  view plain copy print ?
  1. public void shutdown() {  
  2.     httpConnection.close();  
  3. }  

方法三: 
实例化代码使用:HttpClient client = new HttpClient(); 
在method.releaseConnection();之后加上 
client.getHttpConnectionManager().closeIdleConnections(0);此方法源码代码如下: 

[java]  view plain copy print ?
  1. public void closeIdleConnections(long idleTimeout) {  
  2.     long maxIdleTime = System.currentTimeMillis() - idleTimeout;  
  3.     if (idleStartTime <= maxIdleTime) {  
  4.         httpConnection.close();  
  5.     }  
  6. }  

将idleTimeout设为0可以确保链接被关闭。 
以上这三种方法都是有客户端主动关闭TCP链接的方法。下面再介绍由服务器端自动关闭链接的方法。 
方法四: 
代码实现很简单,所有代码就和最上面的事例代码一样。只需要在HttpMethod method = new GetMethod("http://www.apache.org");加上一行HTTP头的设置即可 

[java]  view plain copy print ?
  1. method.setRequestHeader("Connection""close");  

看一下HTTP协议中关于这个属性的定义: 
HTTP/1.1 defines the "close" connection option for the sender to signal that the connection will be closed after completion of the response. For example, 
       Connection: close 
现在再说一下客户端关闭链接和服务器端关闭链接的区别。如果采用客户端关闭链接的方法,在客户端的机器上使用netstat –an命令会看到很多TIME_WAIT的TCP链接。如果服务器端主动关闭链接这中情况就出现在服务器端。 
参考WIKI上的说明http://wiki.apache.org/HttpComponents/FrequentlyAskedConnectionManagementQuestions 
The TIME_WAIT state is a protection mechanism in TCP. The side that closes a socket connection orderly will keep the connection in state TIME_WAIT for some time, typically between 1 and 4 minutes. 
TIME_WAIT的状态会出现在主动关闭链接的这一端。TCP协议中TIME_WAIT状态主要是为了保证数据的完整传输。具体可以参考此文档: 
http://www.softlab.ntua.gr/facilities/documentation/unix/unix-socket-faq/unix-socket-faq-2.html#ss2.7 
另外强调一下使用上面这些方法关闭链接是在我们的应用中明确知道不需要重用链接时可以主动关闭链接来释放资源。如果你的应用是需要重用链接的话就没必要这么做,使用原有的链接还可以提供性能。

 

原文来自:http://www.javaeye.com/topic/234759

<think>我们正在学习HttpClient框架的使用。根据引用[1]和[2],HttpClient是Apache软件基金会的一个开源项目,用于处理HTTP通信。它提供了高效、可靠的HTTP通信能力,支持连接池管理、多线程安全、HTTPS等特性。我们将从基础开始,逐步介绍如何使用HttpClient。 ### 1. 添加依赖 首先,在项目中添加HttpClient依赖。如果使用Maven,在pom.xml中添加(注意版本号,这里以4.5.3为例,但建议使用最新稳定版本): ```xml <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.13</version> <!-- 请检查最新版本 --> </dependency> ``` ### 2. 创建HttpClient实例 HttpClient实例是线程安全的,建议重复使用同一个实例。可以通过`HttpClientBuilder`创建: ```java import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; public class HttpClientExample { public static void main(String[] args) { // 创建HttpClient实例 try (CloseableHttpClient httpClient = HttpClients.createDefault()) { // 使用httpClient执行请求 } catch (Exception e) { e.printStackTrace(); } } } ``` ### 3. 执行HTTP请求 下面以GET请求为例,展示如何发送请求并处理响应: ```java import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; public class GetExample { public static void main(String[] args) { try (CloseableHttpClient httpClient = HttpClients.createDefault()) { HttpGet httpGet = new HttpGet("https://example.com"); try (CloseableHttpResponse response = httpClient.execute(httpGet)) { // 获取响应状态码 int statusCode = response.getStatusLine().getStatusCode(); System.out.println("Status Code: " + statusCode); // 获取响应内容 String responseBody = EntityUtils.toString(response.getEntity()); System.out.println("Response Body: " + responseBody); } } catch (Exception e) { e.printStackTrace(); } } } ``` ### 4. 处理POST请求 发送POST请求需要构建请求体。以下示例发送JSON数据: ```java import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; public class PostExample { public static void main(String[] args) { try (CloseableHttpClient httpClient = HttpClients.createDefault()) { HttpPost httpPost = new HttpPost("https://example.com/api"); // 设置请求头 httpPost.setHeader("Content-Type", "application/json"); // 设置请求体 String json = "{\"username\":\"admin\",\"password\":\"123456\"}"; httpPost.setEntity(new StringEntity(json)); try (CloseableHttpResponse response = httpClient.execute(httpPost)) { // 处理响应 System.out.println("Status Code: " + response.getStatusLine().getStatusCode()); System.out.println("Response Body: " + EntityUtils.toString(response.getEntity())); } } catch (Exception e) { e.printStackTrace(); } } } ``` ### 5. 高级特性 - **连接池管理**:通过配置连接池提高性能。使用`PoolingHttpClientConnectionManager`可以管理多个连接。 - **HTTPS支持**:HttpClient默认支持HTTPS,但可能需要配置SSL上下文以处理自签名证书等。 - **Cookie管理**:使用`CookieStore`和`HttpClientContext`来管理Cookie。 ### 6. 实战示例:使用连接池 ```java import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; public class ConnectionPoolExample { public static void main(String[] args) { // 创建连接池管理器 PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(); cm.setMaxTotal(200); // 最大连接数 cm.setDefaultMaxPerRoute(20); // 每个路由最大连接数 // 创建HttpClient try (CloseableHttpClient httpClient = HttpClients.custom() .setConnectionManager(cm) .build()) { // 使用httpClient执行多个请求 } catch (Exception e) { e.printStackTrace(); } } } ``` ### 7. 注意事项 - **资源释放**:确保关闭`CloseableHttpResponse`和`CloseableHttpClient`,避免资源泄漏。 - **异常处理**:妥善处理IO异常和HTTP状态异常。 - **性能优化**:合理配置连接池参数,避免频繁创建和销毁连接。 --- 通过以上步骤,你可以开始使用HttpClient进行HTTP通信。更多高级用法请参考官方文档[^1][^2]。 §§相关问题§§ 1. 如何在HttpClient中处理HTTPS请求并忽略证书验证? 2. 如何使用HttpClient进行文件上传? 3. HttpClient如何设置超时时间? 4. 如何通过HttpClient实现重试机制? 5. 如何使用HttpClient进行身份认证(如Basic Auth)?</think>### Apache HttpClient 学习与使用指南 Apache HttpClient 是 Java 领域最强大的 HTTP 客户端库之一,支持 HTTP/1.1、HTTP/2、HTTPS、连接池、Cookie 管理等高级特性。以下是核心使用指南: --- ### 一、快速入门 #### 1. 添加依赖(Maven) ```xml <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.13</version> <!-- 检查最新版本 --> </dependency> ``` #### 2. 发送 GET 请求 ```java import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; public class BasicGetExample { public static void main(String[] args) throws Exception { try (CloseableHttpClient httpClient = HttpClients.createDefault()) { HttpGet httpGet = new HttpGet("https://api.example.com/data"); try (CloseableHttpResponse response = httpClient.execute(httpGet)) { // 获取状态码 int statusCode = response.getStatusLine().getStatusCode(); // 获取响应内容 String responseBody = EntityUtils.toString(response.getEntity()); System.out.println("Status: " + statusCode); System.out.println("Response: " + responseBody); } } } } ``` --- ### 二、核心功能详解 #### 1. 连接池管理 通过连接池提升性能: ```java PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(); cm.setMaxTotal(200); // 最大连接数 cm.setDefaultMaxPerRoute(20); // 每个路由的最大连接数 try (CloseableHttpClient httpClient = HttpClients.custom() .setConnectionManager(cm) .build()) { // 执行请求... } ``` #### 2. 处理 HTTPS 忽略证书验证(仅测试环境使用): ```java SSLContext sslContext = SSLContexts.custom() .loadTrustMaterial((chain, authType) -> true) // 信任所有证书 .build(); try (CloseableHttpClient httpClient = HttpClients.custom() .setSSLContext(sslContext) .setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE) .build()) { // 执行HTTPS请求... } ``` #### 3. 请求超时控制 ```java RequestConfig config = RequestConfig.custom() .setConnectTimeout(5000) // 连接超时(ms) .setSocketTimeout(10000) // 数据传输超时(ms) .build(); HttpGet httpGet = new HttpGet("https://api.example.com"); httpGet.setConfig(config); ``` #### 4. 表单提交(POST) ```java HttpPost httpPost = new HttpPost("https://api.example.com/login"); List<NameValuePair> params = new ArrayList<>(); params.add(new BasicNameValuePair("username", "test")); params.add(new BasicNameValuePair("password", "123456")); httpPost.setEntity(new UrlEncodedFormEntity(params)); try (CloseableHttpResponse response = httpClient.execute(httpPost)) { // 处理响应... } ``` #### 5. 文件上传 ```java HttpPost httpPost = new HttpPost("https://api.example.com/upload"); FileBody fileBody = new FileBody(new File("test.txt")); MultipartEntityBuilder builder = MultipartEntityBuilder.create(); builder.addPart("file", fileBody); httpPost.setEntity(builder.build()); ``` --- ### 三、高级特性 #### 1. Cookie 管理 ```java CookieStore cookieStore = new BasicCookieStore(); try (CloseableHttpClient httpClient = HttpClients.custom() .setDefaultCookieStore(cookieStore) .build()) { // 首次登录(存储Cookie) HttpPost login = new HttpPost("https://api.example.com/login"); // ... 执行登录 // 后续请求自动携带Cookie HttpGet profile = new HttpGet("https://api.example.com/profile"); httpClient.execute(profile); } ``` #### 2. 异步请求 ```java Future<HttpResponse> future = httpClient.execute( new HttpGet("https://api.example.com/async"), new FutureCallback<HttpResponse>() { @Override public void completed(HttpResponse response) { System.out.println("请求完成"); } @Override public void failed(Exception ex) { ex.printStackTrace(); } @Override public void cancelled() { System.out.println("请求取消"); } } ); ``` --- ### 四、最佳实践 1. **资源释放** 确保关闭 `CloseableHttpResponse` 和 `CloseableHttpClient`(使用 try-with-resources)。 2. **异常处理** 捕获 `IOException` 和 `ClientProtocolException`。 3. **连接复用** 避免频繁创建/销毁 HttpClient 实例,使用连接池[^1]。 4. **日志调试** 添加日志依赖查看网络交互细节: ```xml <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.17.1</version> </dependency> ``` --- ### 五、实战示例:API 测试框架 ```java public class ApiTester { private final CloseableHttpClient httpClient; public ApiTester() { this.httpClient = HttpClients.custom() .setConnectionManager(new PoolingHttpClientConnectionManager()) .build(); } public String get(String url) throws IOException { HttpGet request = new HttpGet(url); try (CloseableHttpResponse response = httpClient.execute(request)) { return EntityUtils.toString(response.getEntity()); } } public int post(String url, Map<String, String> params) throws IOException { HttpPost request = new HttpPost(url); List<NameValuePair> nvpList = new ArrayList<>(); params.forEach((k, v) -> nvpList.add(new BasicNameValuePair(k, v))); request.setEntity(new UrlEncodedFormEntity(nvpList)); try (CloseableHttpResponse response = httpClient.execute(request)) { return response.getStatusLine().getStatusCode(); } } } ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值