Tomcat调优及acceptCountmaxConnections与maxThreads参数的含义和关系

Posted OkidoGreen

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Tomcat调优及acceptCountmaxConnections与maxThreads参数的含义和关系相关的知识,希望对你有一定的参考价值。

maxThreads(包括minSpareThreads)是tomcat工作线程池的配置参数,maxThreads就相当于jdk线程池的maxPoolSize,而minSpareThreads就相当于jdk线程池的corePoolSize,线程任务就是一个连接的请求,,每一个请求都会尝试新建要给Worder线程来处理!

acceptCount、maxConnections实际上是tcp层面的参数,因此我们需要先了解TCP建立连接的三次握手时的过程。

1 握手队列

首先,TCP的三次握手和四次挥手的一般流程图如下:

我们在此前就详细讲过了具体的流程(TCP和UDP协议以及TCP的三次握手和四次挥手),这里不再赘述。这里想要说的是,这张图中还有一些TCP连接的细节没有讲到,而这个细节真是我们现在需要学习的!

实际上,对于Client端的一个请求,Client首先发送SYN给服务端,OS(操作系统内核)会把连接信息放到一个SYN队列(SYN Queue)中,SYN队列是一个半连接队列(因为还没有真正的建立TCP连接),同时服务器返回一个SYN+ACK包给客户端,并且在没有收到ACK包时重传,直到超时。

Client收SYN+ACK包后,再次发送ACK包给服务端,三次握手成功,OS会把连接从SYN队列中移除,并创建一个完整的连接,随后将该连接加入Accept队列(Accept Queue)中,Accept队列是一个全连接队列(因为此时已经真正的建立了TCP连接),等待被上层应用程序取走的连接。当应用程序进程调用accept()系统从accept队列中获取已经建立成功的连接套接字之后,这个socket将移除Accept队列。

如下图所示:

如果Accept全队列满了之后,即使Client继续向server发送ACK的包,服务端会通过/proc/sys/net/ipv4/tcp_abort_on_overflow来决定如何响应:

# cat /proc/sys/net/ipv4/tcp_abort_on_overflow
0

0表示Server直接丢弃该ACK,Server过一段时间再次发送SYN+ACK给Client(也就是重新走握手的第二步),如果Client超时等待比较短,就会返回read timeout;1表示发送RST通知Client,Client端会返回connection reset by peer。

如果是SYN半连接队列满了,不开启syncookies的时候,服务端会丢弃新来的SYN包,而Client在多次重发SYN包得不到响应而返回connection timeout错误。但是,当Server端开启了syncookies=1,那么SYN半连接队列就没有逻辑上的最大值了。(常说的syn floods 洪水攻击就是针对半连接队列的,攻击方不停地建连接,但是建连接的时候只做第一步,第二步中攻击方收到server的syn+ack后故意扔掉什么也不做,而Server需要不断重发直至超时,浙江导致SYN队列满其他正常请求无法进来。)

SYN队列和Accept队列都是内核系统中的队列。SYN半连接队列的大小取决于max(64, /proc/sys/net/ipv4/tcp_max_syn_backlog),一般情况下无法通过高级程序语言直接控制,Accept全连接队列的大小取决于min(backlog, somaxconn),somaxconn是一个os级别的系统参数(一般默认为128),而backlog的值则可以由我们的应用程序定义,比如Java相关的Socket和tomcat都可以去定义。

Java的ServerSocket构造器的backlog参数就是指的Accept队列的大小!

/**
 * 创建服务器套接字,绑定到指定的端口。
 */
public ServerSocket(int port) throws IOException 
    this(port, 50, null);


/**
 * 创建服务器套接字并将其绑定到指定的端口号,并指定Accept队列大小backlog
 */
public ServerSocket(int port, int backlog) throws IOException 
    this(port, backlog, null);

可以看到,默认的ServerSocket的backlog为50。

2 tomcat的参数解释

对于tomcat应用程序来说,tomcat使用Acceptor线程专门负责从Accept队列中取出connection,接受该connection,然后交给工作线程去处理(读取请求参数、处理逻辑、返回响应等等)。如果该连接不是keep alived的话,则关闭该连接,然后该工作线程释放回线程池,如果是keep alived的话,则等待下一个数据包的到来直到keepAliveTimeout,然后关闭该连接释放回线程池。然后Acceptor线程接着去accept队列取connection。

  1. maxConnections
    1. 表示最多可以有多少个socket连接到tomcat上,也就是Acceptor能同时接受的最大连接数。当当前socket连接超过maxConnections的时候,Acceptor线程自己会阻塞等待,等连接降下去之后,才去处理Accept队列的下一个连接)。
    2. 对于BIO来说,每一个建立的连接都需要一个Worker线程处理,一般maxConnections取值与maxThreads相同,默认为200(如果配置了Executor,则默认值是Executor的maxThreads)
    3. 对于NIO 和 NIO2,由于采用了NIO的IO多路复用模型,一个Poller线程中的Selector可以同时接受和处理多个socket连接并监听事件,只有当连接中有的请求到来时才会使用Worker线程去处理。maxConnections应该配置得比maxThreads要大的多,默认情况下是10000。
    4. 对于APR/native默认值为 8192,并且如果配置的值不是1024的倍数,maxConnections 的实际值将减少到1024的最大倍数。
  2. acceptCount
    1. acceptCount指的就是Accept队列的大小,即OS维护的TCP完全连接队列的大小。换句话说,就是指当前同时连接数超过maxConnections的时候,还可接受的连接数。默认值为100。
    2. 当accept队列满了之后,client可能会返回read timeout 或者 connection reset by peer(tomcat官网说的是返回connection refused)。
  3. maxThreads
    1. tomcat的Connector工作线程池的最大并发线程数量,即同一时刻tomcat最多maxThreads个worker线程在处理通过Acceptor接收的客户端的请求。这是真正的tomcat并发数。
    2. maxThreads规定的是最大的线程数目,并不是实际running的CPU数量;实际上,maxThreads的大小比CPU核心数量要大得多。这是因为,处理请求的线程真正用于计算的时间可能很少,大多数时间可能在阻塞,如等待数据库返回数据、等待硬盘读写数据等。因此,在某一时刻,只有少数的线程真正的在使用物理CPU,大多数线程都在等待;因此线程数远大于物理核心数才是合理的。换句话说,Tomcat通过使用比CPU核心数量多得多的线程数,可以使CPU忙碌起来,大大提高CPU的利用率。
    3. 总之,maxThreads数量应该远大于CPU核心数量;而且CPU核心数越大,maxThreads应该越大;应用中CPU越不密集(IO越密集),maxThreads应该越大,以便能够充分利用CPU。当然,maxThreads的值并不是越大越好,如果maxThreads过大,那么CPU会花费大量的时间用于线程的切换,整体效率会降低。线程数的经验值为:1核2g内存为200,线程数经验值200;4核8g内存,线程数经验值800。

参考资料:

  1. Apache Tomcat 8 Configuration Reference
  2. 深入理解-Tomcat-(二)-从宏观上理解-Tomcat-组件及架构
  3. 深度解读Tomcat中的NIO模型
  4. 详解tomcat的连接数与线程池

=========================================================================

Tomcat中Max-thread配置项对TPS的影响

压测指标以及对应含义:

压力工具中的线程数和用户数与 TPS的关系?
Jmeter中线程数即虚拟用户数量;TPS即每秒事务数。在性能测试过程中,TPS 之所以重要,是因为它可以反应出一个系统的处理能力。

我们常说的并发是什么?

我们要先说明"并发"这个概念是靠什么数据来承载的。在上面的内容中,我们说了好多的指标,但并发是需要具体的指标来承载的。你可以说,我的并发是 1000TPS,或者 1000RPS,或者 1000HPS,这都随便你去定义(一般我们使用TPS来定义这个概念)。但是在一个具体的项目中,当你说到并发 1000 这样没有单位的词时,一定要让大家都能理解这是什么。比如一个线程平均处理请求的时间是250ms,压力工具是 4 个并发线程,由于每个线程都可以在一秒内完成 4 个事务,所以总的 TPS 是 16。

本次压测过程中,主要监控的几项指标: tps,响应时间,错误数,线程数,cpu使用率,jvm内存;

tps,响应时间,错误率可以使用jmeter来进行压测,结合报告进行判断;

线程数,cpu使用率,jvm内存则使用jconsole来进行监控。

jmeter相关参数配置以及指标

测试场景:

使用梯度施压的测试场景,最大压力值为600虚拟用户,每次增加30线程,每次施压持续一分钟。

Jconsole监控tomcat性能指标;

在jdk安装目录下,打开jconsole.exe可执行程序,即可看到如下界面;

这边可以选择本地进程以及远程进程,远程进程则需要配置

export JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote"

export JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote.port=9001"

export JAVA_OPTS="$JAVA_OPTS -Djava.rmi.server.hostname=x.x.x.x"

export JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote.ssl=false"

export JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote.authenticate=false"

正确配置后即可看到应用程序的一个概况:

tomcat参数调整:

Tomcat主要负责两件事:

1、处理 Socket 连接,负责网络字节流与 Request 和 Response 对象的转化。

2、加载和管理 Servlet,以及具体处理 Request 请求

因此。Tomcat设计了两个核心组件连接器(Connector)和容器(Container)来分别做这两件事情。连接器负责对外交流,容器负责内部处理。

 

 连接器用 ProtocolHandler来处理网络连接和应用层协议,包含了 2 个重要部件:

Endpoint 接收到 Socket 连接后,生成一个 SocketProcessor 任务提交到线程池去处理,SocketProcessor 的 run 方法会调用 Processor 组件去解析应用层协议,Processor 通过解析生成 Request 对象后,会调用 Adapter 的 Service 方法。

Tomcat关于ProtocolHandler的实现主要有NIO/NIO2/APR三种方式,分别代表非阻塞io,异步io,APR 本地库实现的非阻塞io;

因此Tomcat调优主要基于两个方向:

线程池参数

accept-count:最大等待数

官方文档的说明为:当所有的请求处理线程都在使用时,所能接收的连接请求的队列的最大长度。当队列已满时,任何的连接请求都将被拒绝。accept-count的默认值为100。

详细的来说:当调用HTTP请求数达到tomcat的最大线程数时,还有新的HTTP请求到来,这时tomcat会将该请求放在等待队列中,这个acceptCount就是指能够接受的最大等待数,默认100。如果等待队列也被放满了,这个时候再来新的请求就会被tomcat拒绝(connection refused)。

maxThreads:最大线程数

每一次HTTP请求到达Web服务,tomcat都会创建一个线程来处理该请求,那么最大线程数决定了Web服务容器可以同时处理多少个请求。maxThreads默认200,肯定建议增加。但是,增加线程是有成本的,更多的线程,不仅仅会带来更多的线程上下文切换成本,而且意味着带来更多的内存消耗。JVM中默认情况下在创建新线程时会分配大小为1M的线程栈,所以,更多的线程异味着需要更多的内存。

maxConnections:最大连接数

官方文档的说明为:

这个参数是指在同一时间,tomcat能够接受的最大连接数。对于Java的阻塞式BIO,默认值是maxthreads的值;如果在BIO模式使用定制的Executor执行器,默认值将是执行器中maxthreads的值。对于Java 新的NIO模式,maxConnections 默认值是10000。

对于windows上APR/native IO模式,maxConnections默认值为8192,这是出于性能原因,如果配置的值不是1024的倍数,maxConnections 的实际值将减少到1024的最大倍数。

如果设置为-1,则禁用maxconnections功能,表示不限制tomcat容器的连接数。

maxConnections和accept-count的关系为:当连接数达到最大值maxConnections后,系统会继续接收连接,但不会超过acceptCount的值。

maxConnections、maxThreads、acceptCount关系

用一个形象的比喻,通俗易懂的解释一下tomcat的最大线程数(maxThreads)、最大等待数(acceptCount)和最大连接数(maxConnections)三者之间的关系。我们可以把tomcat比做一个火锅店,流程是取号、入座、叫服务员,可以做一下三个形象的类比:

(1)acceptCount 最大等待数

可以类比为火锅店的排号处能够容纳排号的最大数量;排号的数量不是无限制的,火锅店的排号到了一定数据量之后,服务往往会说:已经客满。

(2)maxConnections 最大连接数

可以类比为火锅店的大堂的餐桌数量,也就是可以就餐的桌数。如果所有的桌子都已经坐满,则表示餐厅已满,已经达到了服务的数量上线,不能再有顾客进入餐厅了。

(3)maxThreads:最大线程数

可以类比为厨师的个数。每一个厨师,在同一时刻,只能给一张餐桌炒菜,就像极了JVM中的一条线程。

整个流程如下:

(1)取号:如果maxConnections连接数没有满,就不需要取号,因为还有空余的餐桌,直接被大堂服务员领上餐桌,点菜就餐即可。如果 maxConnections 连接数满了,但是取号人数没有达到 acceptCount,则取号成功。如果取号人数已达到acceptCount,则拿号失败,会得到Tomcat的Connection refused connect 的回复信息。

(2)上桌:如果有餐桌空出来了,表示maxConnections连接数没有满,排队的人,可以进入大堂上桌就餐。

(3)就餐:就餐需要厨师炒菜。厨师的数量,比顾客的数量,肯定会少一些。一个厨师一定需要给多张餐桌炒菜,如果就餐的人越多,厨师也会忙不过来。这时候就可以增加厨师,一增加到上限maxThreads的值,如果还是不够,只能是拖慢每一张餐桌的上菜速度,这种情况,就是大家常见的"上一道菜吃光了,下一道菜还没有上"尴尬场景。

IO模型选择

I/O 调优实际上是连接器类型的选择,一般情况下默认都是 NIO,在绝大多数情况下都是够用的,除非你的 Web 应用用到了 TLS 加密传输,而且对性能要求极高,这个时候可以考虑 APR,因为 APR 通过 OpenSSL 来处理 TLS 握手和加 / 解密。OpenSSL 本身用 C 语言实现,它还对 TLS 通信做了优化,所以性能比 Java 要高。如果你的 Tomcat 跑在 Windows 平台上,并且 HTTP 请求的数据量比较大,可以考虑 NIO.2,这是因为 Windows 从操作系统层面实现了真正意义上的异步 I/O,如果传输的数据量比较大,异步 I/O 的效果就能显现出来。如果你的 Tomcat 跑在 Linux 平台上,建议使用 NIO,这是因为 Linux 内核没有很完善地支持异步 I/O 模型,因此 JVM 并没有采用原生的 Linux 异步 I/O,而是在应用层面通过 epoll 模拟了异步 I/O 模型,只是 Java NIO 的使用者感觉不到而已。因此可以这样理解,在 Linux 平台上,Java NIO 和 Java NIO.2 底层都是通过 epoll 来实现的,但是 Java NIO 更加简单高效。

Springboot中,可通过如下来配置其他protocol

经测试,在设定的几种测试场景中NIO,NIO2对性能影响不大。所以本文使用nio为io模型进行测试;

确定了tomcat调整的方向,我们即可制定几组测试数据来进行压测,观察配置项对各指标的影响,

测试类代码如下:

即sleep 1000ms,然后使用jdbctemplate进行一次数据库查询;

首先我们观察max-thread对并发的影响;

这里配置max-thread = 50;max-thread=200两组数据,进行梯度压测

max-thread = 50

响应时间:

 线程数:

 TPS:

 请求响应时间:

 线程数和响应时间:

 

 通过响应时间和线程数图可以看到,线程达到50左右后,响应时间上升的斜度开始升高,说明到达瓶颈,从tps图中可以看到,程序的并发峰值达到50tps左右,在50线程左右达到最高,而后趋于稳定;通过请求与响应时间图可以看到,当每秒请求(rps)达到35-40左右时, 开始出现响应时间波动;通过线程数与响应时间可以看到,当活动线程数达到50左右,响应时间开始出现升高;

max-thread = 200

响应时间:

 线程数:

 TPS:

 请求响应时间:

 线程数和响应时间:

 

 通过响应时间和线程数图可以看到,线程达到200左右后,响应时间开始升高,说明性能存在瓶颈,从tps图中可以看到,程序的并发峰值达到200tps左右,在200线程左右达到最高,而后趋于稳定;通过请求与响应时间图可以看到,当每秒请求(rps)达到190左右时, 开始出现响应时间波动;通过线程数与响应时间可以看到,当活动线程数达到200左右,响应时间开始出现升高;

因此可以得到结论,当系统达到瓶颈时,通过提高max-thread可提升系统的吞吐量,TPS=( max-thread/平均响应时间) ,比如接口响应时间平均为20ms,则50的最大thread可达到2500左右的tps,每个线程每秒钟可处理50个请求;但如果盲目调大最大线程数,会造成大量的线程切换,导致吞吐量无法达到预期的值(如下图所示,设置最大线程数为2000,并用2000的虚拟用户进行压测,系统吞吐量在1700左右:由于线程数为2000,但由于大量线程之间切换,平均响应时间却超过了1s,所以导致每秒事务处理量无法到达预期

带宽对性能的影响:

假设服务器带宽为10Mbps,则每秒1.25MB的下载速度。

当对服务器进行压力测试时,如果服务器各项指标利用率还比较低,而tps已经达到瓶颈,则可以先考虑带宽的因素;

tomcat最大并发,是不是就是maxThread配置的数值?

不是。假设接口响应时间在50ms,则每个线程1秒钟可以处理20个请求,则tps为20*maxthreads

以上是关于Tomcat调优及acceptCountmaxConnections与maxThreads参数的含义和关系的主要内容,如果未能解决你的问题,请参考以下文章

Tomcat7调优及JVM性能优化for Linux环境

Tomcat7调优及JVM性能优化for Linux环境

Tomcat调优及acceptCountmaxConnections与maxThreads参数的含义和关系

Tomcat调优及acceptCountmaxConnections与maxThreads参数的含义和关系

Haproxy服务调优及配置内容详解

Hive调优及原理分析