• 可观测性-Metrics-Tomcat连接数、线程数理解以及压测记录


    背景

    为了搞懂Tomat的连接+线程模型,搞清楚每个配置参数的作用,实际压测看一下是否与预期一致。

    Tomcat配置如下

    server:
      # tomcat配置
      tomcat:
        # 允许最大连接数,默认8192,当达到临界值时,系统会基于accept-count继续接受连接
        max-connections: 50
        # 当所有线程都在使用时,建立连接的请求的等待队列长度,默认100
        accept-count: 10
        # 连接器在关闭空闲连接之前等待的毫秒数,默认 20000 20s
        connection-timeout: 20s
        uri-encoding: UTF-8
        threads:
          # 线程池的最小工作线程数,默认10
          min-spare: 5
          # 线程池的最大线程数,默认200
          max: 5
        mbeanregistry:
          # 是否启用Tomcat的MBean注册表
          enabled: true
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    测试接口伪代码

    controller {
        /get
        String get(){
            Sleep(10s);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    原理

    Tomcat的NioEndpoint实现了I/O多路复用模型。

    Java的多路复用器的使用:

    • 创建一个Selector,在其上注册感兴趣的事件,然后调用select方法,等待感兴趣的事情发生
    • 感兴趣的事情发生了,比如可读了,就创建一个新的线程从Channel中读数据

    NioEndpoint包含LimitLatch、Acceptor、Poller、SocketProcessor和Executor共5个组件。

    img

    此处来自:https://www.jianshu.com/p/eb1711261986

    线程模型Tomcat源码如下

    org.apache.tomcat.util.net.NioEndPoint.startInternal
    - org.apache.tomcat.util.net.AbstractEndpoint.createExecutor
    	- org.apache.tomcat.util.threads.ThreadPoolExecutor
    	- org.apache.tomcat.util.threads.TaskQueue
    	
    AbstractEndpoint.java
    public void createExecutor() {
        internalExecutor = true;
        TaskQueue taskqueue = new TaskQueue();
        // http-nio-8080-exec-
        TaskThreadFactory tf = new TaskThreadFactory(getName() + "-exec-", daemon, getThreadPriority());
        // 这里的ThreadPoolExecutor是org.apache.tomcat.util.threads包下的。
        executor = new ThreadPoolExecutor(getMinSpareThreads(), getMaxThreads(), 60, TimeUnit.SECONDS,taskqueue, tf);
        taskqueue.setParent( (ThreadPoolExecutor) executor);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • Tomcat通过继承JDK的ThreadPoolExecutor实现自己的ThreadPoolExecutor,记录已提交但尚未完成的任务数 submittedCount

    • Tomcat自定义TaskQueue取代JDK中几种队列,配合上面的submittedCount,实现JDK线程池增强,实现新增线程池到Coresize -> maxSize -> queue.

    • server.tomcat.threads.maxserver.tomcat.threads.min-spare配置线程池。

    maxConnections:最大连接数
    这个参数是指tomcat能够接受的最大连接数。当Tomcat接收的连接数达到maxConnections时,Acceptor线程不会读取accept队列中的连接;这时accept队列中的线程会一直阻塞着,直到Tomcat接收的连接数小于maxConnections。如果设置为-1,则连接数不受限制。
    默认值与连接器使用的协议有关:NIO的默认值是10000?8192,APR/native的默认值是8192,而BIO的默认值为maxThreads(如果配置了Executor,则默认值是Executor的maxThreads)。

    (注意:在windows下,APR/native的maxConnections值会自动调整为设置值以下最大的1024的整数倍;如设置为2000,则最大值实际是1024。)
    当连接数达到最大值maxConnections后,系统会继续接收连接,但不会超过acceptCount的值.

    即Tomcat所能处理的最大连接数为 maxConnections + acceptCount

    • serverSock.socket().bind(addr,getAcceptCount());
    • LimitLatch作用也是对最大连接数的限制
    org.apache.tomcat.util.net.AbstractEndpoint
    private int maxConnections = 8*1024;
    public void setMaxConnections(int maxCon) {
        this.maxConnections = maxCon;
        LimitLatch latch = this.connectionLimitLatch;
        if (latch != null) {
            // Update the latch that enforces this
            if (maxCon == -1) {
                releaseConnectionLatch();
            } else {
                latch.setLimit(maxCon);
            }
        } else if (maxCon > 0) {
            initializeConnectionLatch();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    再来个好理解的图:

    Tomcat同时可以处理的连接数目是maxConnections,但服务器中可以同时接收的连接数为maxConnections+acceptCount 。acceptCount的设置,与应用在连接过高情况下希望做出什么反应有关系。如果设置过大,后面进入的请求等待时间会很长;如果设置过小,后面进入的请求立马返回connection refused。

    总结下总体流程

    启动时,Tomcat 将根据设置的值minSpareThreads创建初始线程,并根据需要增加线程数,直到maxThreads。 如果达到最大线程数,并且所有线程都忙,则服务器将只继续接受一定数量的并发连接maxConnections。一旦达到该限制,新连接将被放入队列 ( acceptCount) 中以等待下一个可用线程。当连接数达到maxConnections并且队列(acceptCount)已满时,任何额外的传入客户端将开始接收Connection Refused错误。

    如果您的服务器开始生成这些错误,您将需要调整连接器的线程池容量以更好地适应传入请求的数量。

    但是,请注意,如果您将最大线程数和最大队列长度 ( acceptCount) 的值设置得太高,则服务器流量的增加可能会快速消耗过多的服务器资源,因为它会填满队列并用完可用的线程数。

    压测

    Tomcat配置如下

    压测的接口Sleep(10s)

    server:
      # tomcat配置
      tomcat:
        # 允许最大连接数,默认8192,当达到临界值时,系统会基于accept-count继续接受连接
        max-connections: 50
        # 当所有线程都在使用时,建立连接的请求的等待队列长度,默认100
        accept-count: 10
        # 连接器在关闭空闲连接之前等待的毫秒数,默认 20000 20s
        connection-timeout: 20s
        uri-encoding: UTF-8
        threads:
          # 线程池的最小工作线程数,默认10
          min-spare: 5
          # 线程池的最大线程数,默认200
          max: 5
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    应关注的metrics

    指标定义
    tomcat.threads.busytomcat 繁忙线程
    tomcat.threads.currenttomcat 当前线程数(包括守护线程)
    tomcat.threads.config.maxtomcat 配置的线程最大数
    tomcat.connections.current这个默认没有,补上。
    tomcat.connections.max这个默认没有,补上。

    connectionCount

    org.apache.tomcat.util.net.AbstractEndpoint
    - getConnectionCount
    
    • 1
    • 2

    相关代码:https://gitee.com/lakernote/easy-admin

    5个并发压测

    期望:因为Tomcat配置的线程数刚好是5,所以RT都是10s左右

    实际:RT都是10s左右

    10个并发压测

    期望:因为Tomcat配置的线程数刚好是5,会有5个连接排队等10s,所以RT应该是5个10s左右,5个20s左右。平均值为15s左右

    实际:平均RT是15s左右

    60个并发压测

    期望:因为Tomcat配置maxConnections+acceptCount = 50 + 10 =60,应该刚好不会出现Connection Refused错误,但是有大量RT很长。

    实际:没有错误。

    当前连接数

    61个并发压测

    期望:因为Tomcat配置maxConnections+acceptCount = 50 + 10 =60,会出现一个Connection Refused错误,有大量RT很长。

    实际:有一个错误。

    参考:

    • https://www.datadoghq.com/blog/tomcat-architecture-and-performance/
    • https://www.cnblogs.com/sandyflower/p/15495419.html
  • 相关阅读:
    git初识
    BioVendor sRAGE抗体解决方案
    基于JAVA学生信息管理和新生报到系统(Springboot框架) 开题报告
    一起Talk Android吧(第三百七十四回:多线程之大结局)
    Cookie和session 及Web相关工具
    JS中的类相关操作
    二叉树的遍历(非递归版)
    c#入参使用引用类型为啥要加ref?
    微信小程序在线考试项目开发-注册登录功能
    【无标题】
  • 原文地址:https://blog.csdn.net/abu935009066/article/details/128198456