在理解httpclient问题的同时,碰到新问题

本文深入探讨了HTTPClient的连接管理和KeepAlive机制,通过实例分析了连接池的大小、超时设置及资源关闭的重要性。特别关注了多线程请求下连接的复用与关闭,以及服务器配置对连接保持的影响。

写这边文章时,背景如下

1.为了分析之前的一个tcp close wait问题

2.很多项目对httpclient的参数和使用都理解有问题,往往随便写一个或者使用网上的代码。导致在一些场景的tcp问题,为了规范化和正确的使用httpclient

大致有这些版本

  • httpclient3.x
  • httpclient4.x到httpclient4.3以下
  • httpclient4.3以上

一般会选择4.3以上

https://www.baeldung.com/httpclient-connection-management 这是国外一个教学网站的说明,使用和说明还算比较ok

httpclient
public class HttpClientUtils {
 
    private static HttpClientContext context = HttpClientContext.create();
 
    private static CloseableHttpClient httpClient;
 
 
    static {
 
        // 基本配置
        RequestConfig requestConfig = RequestConfig.custom()
                // 建连超时时间
                .setConnectTimeout(5000)
                // 传输超时时间
                .setSocketTimeout(3000)
                // 从连接池获取连接超时时间
                .setConnectionRequestTimeout(10000)
                .build();
 
        // 连接管理器
        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
        // 最大连接数
        cm.setMaxTotal(80);
        // 同一个路由最大连接数
        cm.setDefaultMaxPerRoute(20);
 
        // 客户端构造器
        HttpClientBuilder clientBuilder = HttpClients
                .custom()
                .setConnectionManager(cm)
                .setConnectionManagerShared(false)// 影响连接的关闭
                .setDefaultRequestConfig(requestConfig);
 
        httpClient = clientBuilder.build();
    }
 
    public static byte[] get(String url, String userAgent) throws IOException {
        HttpGet httpGet = new HttpGet(url);
        if (userAgent != null) {
            httpGet.setHeader(HTTP.USER_AGENT, userAgent);
        }
        CloseableHttpResponse response = httpClient.execute(httpGet, context);
 
        System.out.println("----------------------------------------");
        System.out.println(response.getStatusLine());
 
        HttpEntity entity = response.getEntity();
 
        if (entity != null) {
            try {
                return IOUtils.toByteArray(entity.getContent());
            } finally {
                // 只需在传输完毕关闭流即可,详细见源码
                EntityUtils.consumeQuietly(entity);
            }
        }
 
        return null;
    }
 
}

  

连接池的概念很重要。没有池子,每一次都要tcp建连断连,开销比较大。

以上的代码可以最简单的使用httpclient pool,主要关注以下内容

1.timeout和poolsize的设置

2.资源关闭

demo1
1
2
3
4
5
6
7
8
9
10
for  ( int  i =  0 ; i <  20 ; i++) {
     try  {
         System.out.println( new  String(HttpClientUtils.post( "http://10.101.17.26:8888/demo" ),  "utf-8" ));
     catch  (IOException e) {
         e.printStackTrace();
     }
}
 
CountDownLatch latch =  new  CountDownLatch( 1 );

 

客户端

服务器

符合预期

 

多线程请求

demo1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
for  ( int  i =  0 ; i <  100 ; i++) {
     new  Thread( new  Runnable() {
             @Override
             public  void  run() {
                 try  {
                     System.out.println( new  String(HttpClientUtils.post( "http://10.101.17.26:8888/demo" ),  "utf-8" ));
                 catch  (IOException e) {
                     e.printStackTrace();
                 }
             }).start();
     }
}
CountDownLatch latch =  new  CountDownLatch( 1 );

 

20个符合预期

 

刚好为了测试单线程的close情况,于是把thread去掉

demo1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
         for  ( int  i =  0 ; i <  100 ; i++) {
//            new Thread(new Runnable() {
//                @Override
//                public void run() {
                     try  {
                         System.out.println( new  String(HttpClientUtils.post( "http://10.101.17.26:8888/demo" ),  "utf-8" ));
                     catch  (IOException e) {
                         e.printStackTrace();
                     }
//                }
//            }).start();
         }
         CountDownLatch latch =  new  CountDownLatch( 1 );
         latch.await();

连接呢???

经过测试,与循环100这个数有关系于是模拟以下测试

demo1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
for  ( int  i =  0 ; i <  101 ; i++) {
 
     if  (i ==  98  || i ==  99  || i ==  100 ) {
         System.out.println(i);
     }
     try  {
         System.out.println( new  String(HttpClientUtils.post( "http://10.101.17.26:8888/demo" ),  "utf-8" ));
     catch  (IOException e) {
         e.printStackTrace();
     }
 
}
 
CountDownLatch latch =  new  CountDownLatch( 1 );

打断点跟踪发现,reusable这个参数在100的时候,行为是和99和101不同的

 

于是看源码跟踪这个参数修改的地方

private volatile boolean reusable;

代码中有多处将此参数设置为false,其中在100的时候,命中断点。在MainClientExec.java中有如下使用,那么 reuseStrategy.keepAlive(response, context)是关键

继续看 reuseStrategy.keepAlive(response, context),有个叫Porxy-Connection的当100的时候,值为Close状态,证明这个是服务端的一些设置导致的,于是谷歌关键字keepalive

 

找到tomcat有如下参数配置,nginx也有,大致意思就是一个连接能被重复使用的次数,当超过他,就会断开。这也可以解释为什么100的时候,连接没了。101又开始建立新的连接

maxKeepAliveRequests

The maximum number of HTTP requests which can be pipelined until the connection is closed by the server. Setting this attribute to 1 will disable HTTP/1.0 keep-alive, as well as HTTP/1.1 keep-alive and pipelining. Setting this to -1 will allow an unlimited amount of pipelined or keep-alive HTTP requests. If not specified, this attribute is set to 100.

 

转载于:https://www.cnblogs.com/turn2i/p/10480088.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值