tomcat 线程池的使用

XHuiLin 2018-10-25

1,应用背景:项目压力测试,要求同时支持并发数量8000(一秒钟1到2个请求,一直加到8000),5台tomcat配置(apache-tomcat-7.0.72)

线程池配置:

<Executor name="tomcatThreadPool" namePrefix="catalina-exec-" 
        maxThreads="800" minSpareThreads="100" maxSpareThreads="500"  maxQueueSize="100"  prestartminSpareThreads="true" maxIdleTime="60000" />

 connector 配置:

<Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />   -->
        <Connector port="8080"     executor="tomcatThreadPool"
              protocol="org.apache.coyote.http11.Http11NioProtocol"
                  connectionTimeout="40000"  
                  redirectPort="8443" 
                  enableLookups="false"
                  maxPostSize="10485760"
                  URIEncoding="UTF-8"
          acceptCount="1000"
              maxKeepAliveRequests="1"  
                  acceptorThreadCount="2"
          disableUploadTimeout="true" 
          maxConnections="10000"                  
          tcpNoDelay="true"       
           />

 2,概念知识

    1)connector对象

        tomcat 中Connector是用来创建request和response对象,并用于处理请求和处理交换数据的;然后将请求发送给Engine(servlet 容器),Engine处理完成之后再通过Connector 返回给客户端,这也是一个MVC的处理过程。其中Connector可以分为HTTP Connector,AJP Connector等。

 不同版本的tomcat支持不同的Connector,Tomcat 7 支持BIO,NIO和APR

Tomcat 8 支持BIO,NIO,APR和NIO2

Tomcat 9 支持NIO,APR和NIO2

其中APR 是Apache Portable Runtime,是Apache可移植运行库,利用本地库可以实现高可用性,是高并发模式下的首选模式,但是需要安装apr,apri-utils,tomcat-native等包。

tomcat 中的connector可以通过protocol属性来制定使用不同的协议,默认为HTTP/1.1

HTTP/1.1:默认值,使用的协议与Tomcat版本有关

org.apache.coyote.http11.Http11Protocol:BIO

org.apache.coyote.http11.Http11NioProtocol:NIO

org.apache.coyote.http11.Http11Nio2Protocol:NIO2

org.apache.coyote.http11.Http11AprProtocol:APR

在Tomcat7中,自动选取使用BIO或APR(如果找到APR需要的本地库,则使用APR,否则使用BIO);

在Tomcat8中,自动选取使用NIO或APR(如果找到APR需要的本地库,则使用APR,否则使用NIO)。

    2)tomcat 处理请求的过程

在accept队列中接收连接(当客户端向服务器发送请求时,如果客户端与OS完成三次握手建立了连接,则OS将该连接放入accept队列);

在连接中获取请求的数据,生成request;调用servlet容器处理请求;返回response。

其中:连接是TCP层面的(传输层),对应socket;

请求是HTTP层面的(应用层),必须依赖于TCP的连接实现;一个TCP连接中可能传输多个HTTP请求。

   3)socket数量和连接之间的关系

目前大多数HTTP请求使用的是长连接(HTTP/1.1默认keep-alive为true),而长连接意味着,一个TCP的socket在当前请求结束后,如果没有新的请求到来,socket不会立马释放,而是等timeout后再释放。

如果使用BIO,“读取socket并交给Worker中的线程”这个过程是阻塞的,也就意味着在socket等待下一个请求或等待释放的过程中,处理这个socket的工作线程会一直被占用,无法释放;因此Tomcat可以同时处理的socket数目不能超过最大线程数,性能受到了极大限制。

而使用NIO,“读取socket并交给Worker中的线程”这个过程是非阻塞的,当socket在等待下一个请求或等待释放时,并不会占用工作线程,因此Tomcat可以同时处理的socket数目远大于最大线程数,并发性能大大提高。

3  参数含义

   1)acceptCount

accept队列的长度;当accept队列中连接的个数达到acceptCount时,队列满,进来的请求一律被拒绝。默认值是100。

   2) maxConnections

Tomcat在任意时刻接收和处理的最大连接数。当Tomcat接收的连接数达到maxConnections时,Acceptor线程不会读取accept队列中的连接;这时accept队列中的线程会一直阻塞着,直到Tomcat接收的连接数小于maxConnections。如果设置为-1,则连接数不受限制。

默认值与连接器使用的协议有关:NIO的默认值是10000,APR/native的默认值是8192,而BIO的默认值为maxThreads(如果配置了Executor,则默认值是Executor的maxThreads)。

在windows下,APR/native的maxConnections值会自动调整为设置值以下最大的1024的整数倍;如设置为2000,则最大值实际是1024。

   3) maxThreads

请求处理线程的最大数量。默认值是200(Tomcat7和8都是的)。如果该Connector绑定了Executor,这个值会被忽略,因为该Connector将使用绑定的Executor,而不是内置的线程池来执行任务。

maxThreads规定的是最大的线程数目,并不是实际running的CPU数量;实际上,maxThreads的大小比CPU核心数量要大得多。这是因为,处理请求的线程真正用于计算的时间可能很少,大多数时间可能在阻塞,如等待数据库返回数据、等待硬盘读写数据等。因此,在某一时刻,只有少数的线程真正的在使用物理CPU,大多数线程都在等待;因此线程数远大于物理核心数才是合理的。

换句话说,Tomcat通过使用比CPU核心数量多得多的线程数,可以使CPU忙碌起来,大大提高CPU的利用率。

   4)  参数设置

(1)maxThreads的设置既与应用的特点有关,也与服务器的CPU核心数量有关。通过前面介绍可以知道,maxThreads数量应该远大于CPU核心数量;而且CPU核心数越大,maxThreads应该越大;应用中CPU越不密集(IO越密集),maxThreads应该越大,以便能够充分利用CPU。当然,maxThreads的值并不是越大越好,如果maxThreads过大,那么CPU会花费大量的时间用于线程的切换,整体效率会降低。

(2)maxConnections的设置与Tomcat的运行模式有关。如果tomcat使用的是BIO,那么maxConnections的值应该与maxThreads一致;如果tomcat使用的是NIO,那么类似于Tomcat的默认值,maxConnections值应该远大于maxThreads。

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

  

4  Executor 参数

name:该线程池的标记

maxThreads:线程池中最大活跃线程数,默认值200(Tomcat7和8都是)

minSpareThreads:线程池中保持的最小线程数,最小值是25

maxIdleTime:线程空闲的最大时间,当空闲超过该值时关闭线程(除非线程数小于minSpareThreads),单

位是ms,默认值60000(1分钟)

daemon:是否后台线程,默认值true

threadPriority:线程优先级,默认值5

namePrefix:线程名字的前缀,线程池中线程名字为:namePrefix+线程编号

参考文档:http://www.cnblogs.com/kismetv/p/7806063.html

相关推荐