zoukankan      html  css  js  c++  java
  • Tomcat maxKeepAliveRequests

    在写这个问题前,其实我是为了分析项目碰到的一个tcp close wait问题。这个问题就不在这里讲了。

    造成的原因很简单,就是很多项目对httpclient的参数和使用都理解有问题,往往随便写一个或者使用网上的代码。导致在一些场景连接关闭有问题

    httpclient大致有这些版本

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

    一般会选择4.3以上

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

     
    于是我写了一个demo工具类,并对这个工具类做了一些测试
    public class HttpClientUtils {
     
     
        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, HttpClientContext.create());
     
            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;
        }
     
    }
    

      服务端我简单启动了一个web服务(这里略过这个操作),然后客户端用demo进行请求处理

    demo1:客户端单线程多次请求

     
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    for (int i = 0; i < 20; i++) {
        try {
        catch (IOException e) {
            e.printStackTrace();
        }
    }
     
    CountDownLatch latch = new CountDownLatch(1);

    客户端的tcp连接状况

    服务器的tcp连接情况

    符合预期,只有一个连接,连接复用

    demo2:客户端多线程请求

    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);

    客户端tcp连接情况

     100个请求,20个连接,符合预期

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

    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();

    控制台一看,连接呢???一下子可以说是搞不明白的

    20正常100就没了,怀疑和100这个数字安息,于是模拟了以下测试

    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 {
        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.    

     
    如果把这个参数设成1,http1.1就退化成http1.0了 
  • 相关阅读:
    C# 两个List泛型用lamada表达式去重复数据 或者 得到重复数据
    基于模版文件复制替换的abpcore代码生成器(一)
    基于数据库的vs2019的T4模版代码生成器(二)基于mysql数据库
    基于数据库的vs2019的T4模版代码生成器(一)基于sqlserver数据库
    前后端分离开发vue+Elementyui+abpcorewebapi商城管理系统(十六)项目总结
    前后端分离开发vue+Elementyui+abpcorewebapi商城管理系统(十五)项目上线
    前后端分离开发vue+Elementyui+abpcorewebapi商城管理系统(十四)项目优化
    前后端分离开发vue+Elementyui+abpcorewebapi商城管理系统(十三)报表开发
    前后端分离开发vue+Elementyui+abpcorewebapi商城管理系统(十二)订单列表页面
    前后端分离开发vue+Elementyui+abpcorewebapi商城管理系统(十一)之二 添加商品页面
  • 原文地址:https://www.cnblogs.com/turn2i/p/10480088.html
Copyright © 2011-2022 走看看