Apache HttpClient,功能非常强大,据说可以支持峰值一秒7万以上的请求,不过需要注意一些资源释放和超时处理的问题,见下文所总结的几个问题。

问题1:线程资源无法释放,最终导致内存不足、或线程池被用光。

问题代码:

private static HttpClient httpClient = HttpClientBuilder.create().build();
private void sendHttpPost(String httpUrl, Object body) 
{
  try{
      HttpPost request = new HttpPost(httpUrl);
      StringEntity params = new StringEntity(new ObjectMapper().writeValueAsString(body), "UTF-8");
      request.addHeader("content-type", "application/json");
      request.setEntity(params);
      httpClient.execute(request); //问题代码
      } 
      catch (Exception ex) 
     {
      logger.error("Http post error. body={}", body, ex);
     }
}

原因:

httpClient.execute()执行的返回值response没有被close,导致线程一直在等待。

解决:

改用CloseableHttpClient,并保证httpClient.execute()执行的response最终被close掉。而httpClient无需close,可以重复使用。

改正后的代码:

//private static HttpClient httpClient = HttpClientBuilder.create().build();
private static CloseableHttpClient httpClient = HttpClientBuilder.create().build();
private void sendHttpPost(String httpUrl, Object body) 
{
 try {
      HttpPost request = new HttpPost(httpUrl);
      StringEntity params = new StringEntity(new ObjectMapper().writeValueAsString(body), "UTF-8");
      request.addHeader("content-type", "application/json");
      request.setEntity(params);
      //httpClient.execute(request);
      try 
      { 
         CloseableHttpResponse response = httpClient.execute(request)
         //response.close()会被自动调用
         //result = this.extractResultInfo(response);
      }
      catch (Exception ex) 
      {
         logger.error("Http post execute error. body={}", body, ex);
      }
   } 
   catch (Exception ex) 
   {
      logger.error("Http post error. body={}", body, ex);
   }
}

问题2:如果使用HttpClient访问外部url,有时候会出现连接超时或应答超时的情况。如果没有超时处理,也会导致内存不足、或线程池被用光。

问题代码:如上。

解决:创建httpClient时,设置几个timeout时间:

//private static HttpClient httpClient = HttpClientBuilder.create().build();
private static CloseableHttpClient httpClient;// = HttpClientBuilder.create().build(); //no timeout handling...
 
static {
   RequestConfig.Builder requestConfigBuilder = RequestConfig.custom();
   //客户端和服务器建立连接的timeout
   requestConfigBuilder.setConnectTimeout(30000);
   //从连接池获取连接的timeout
   requestConfigBuilder.setConnectionRequestTimeout(30000);
   //连接建立后,request没有回应的timeout
   requestConfigBuilder.setSocketTimeout(30000);
 
   HttpClientBuilder clientBuilder = HttpClientBuilder.create();
   clientBuilder.setDefaultRequestConfig(requestConfigBuilder.build());
   clientBuilder.setDefaultSocketConfig(SocketConfig.custom().setSoTimeout(30000).build()); 
   //连接建立后,request没有回应的timeout
   clientBuilder.setKeepAliveStrategy(new DefaultConnectionKeepAliveStrategy());
   httpClient = clientBuilder.build();

}