`
huchangan2005
  • 浏览: 78296 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

聊下并发和Tomcat线程数--转载

阅读更多

原文地址:http://www.tuicool.com/articles/2qe2Ije

 

本文前半部分结论存在严重错误,请看最后2015-1-20更新部分。

最近一直在解决线上一个问题,表现是:

 

Tomcat每到凌晨会有一个高峰,峰值的并发达到了3000以上,最后的结果是Tomcat线程池满了,日志看很多请求超过了1s。

服务器性能很好,Tomcat版本是7.0.54,配置如下:

<Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
        maxThreads="3000" minSpareThreads="800"/>
    <Connector executor="tomcatThreadPool" port="8084" protocol="org.apache.coyote.http11.Http11AprProtocol"
    connectionTimeout="60000"
    keepAliveTimeout="30000"
    maxKeepAliveRequests="8000"
    maxHttpHeaderSize="8192"
    URIEncoding="UTF-8"
    enableLookups="false"
    acceptCount="1000"
    disableUploadTimeout="true"
    redirectPort="8443" />

事后thread dump看其实真正处于RUNNABLE状态的线程很少,绝大部分线程都处于TIMED_WAITING状态:

于是大伙都开始纠结为什么线程会涨到3000,而且发现即使峰值过了线程数并不会降下来。

我们首先想到的是:

 

后端应用的处理瞬间比较慢,“堵住了”导致前端线程数涨了起来。

但是优化一个版本上线后发现虽然涨的情况有所好转,但是最终线程池还是会达到3000这个最大值。

==================================分割线=========================================

以上是大背景,中间的过程省略,直接跟各位说下目前我得到的结论:

1、首先是为什么线程不释放的问题?

简单说下我验证的Tomcat(7.0.54)线程池大概的工作机制

  • Tomcat启动时如果没有请求过来,那么线程数(都是指线程池的)为0;
  • 一旦有请求,Tomcat会初始化minSapreThreads设置的线程数;
  • Tomcat不会主动对线程池进行收缩,除非确定没有任何请求的时候,Tomcat才会将线程池收缩到minSpareThreads设置的大小;
  • Tomcat6之前的版本有一个maxSpareThreads参数,但是在7中已经移除了, 所以只要前面哪怕只有一个请求,Tomcat也不会释放多于空闲的线程。

至于Tomcat为什么移除maxSpareThreads这个参数,我想也是出于性能的考虑,不停的收缩线程池性能肯定不高,而多余的线程处于等待状态的好处是一有新请求过来立刻可以处理。

  • 而且大量的Tomcat线程处于等待状态不会消耗CPU,但是会消耗一些JVM存储。

补充:上面标红的一句有点问题,进一步验证发现只有使用Keep-Alive(客户端和服务端都支持)时才是这种表现,如果客户端没有使用Keep-Alive那么线程会随着TCP连接的释放而回收。

Tomcat中Keep-Alive相关的参数:

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.

keepAliveTimeout:

 

The number of milliseconds this Connector will wait for another HTTP request before closing the connection. The default value is to use the value that has been set for the  connectionTimeout attribute. Use a value of -1 to indicate no (i.e. infinite) timeout. 

2、为什么线程池会满?

这是我现在纠结的核心。到底是不是应用的性能慢导致的,我现在的结论是有关系,但关键是并发。

  • Tomcat的线程池的线程数跟你的瞬间并发有关系,比如maxThreads设置为1000,当瞬间并发达到1000那么Tomcat就会起1000个线程来处理,这时候跟你应用的快慢关系不大。

那么是不是并发多少Tomcat就会起多少个线程呢?这里还跟Tomcat的这几个参数设置有关系,看官方的解释是最靠谱的:

maxThreads:

 

The maximum number of request processing threads to be created by this Connector , which therefore determines the maximum number of simultaneous requests that can be handled. If not specified, this attribute is set to 200. If an executor is associated with this connector, this attribute is ignored as the connector will execute tasks using the executor rather than an internal thread pool.

maxConnections:

 

The maximum number of connections that the server will accept and process at any given time. When this number has been reached, the server will accept, but not process, one further connection. This additional connection be blocked until the number of connections being processed falls below maxConnections at which point the server will start accepting and processing new connections again. Note that once the limit has been reached, the operating system may still accept connections based on the acceptCount setting. The default value varies by connector type. For BIO the default is the value of  maxThreads unless an  Executor is used in which case the default will be the value of maxThreads from the executor. For NIO the default is  . For APR/native, the default is  .

……

acceptCount:

 

The maximum queue length for incoming connection requests when all possible request processing threads are in use. Any requests received when the queue is full will be refused. The default value is 100.

minSpareThreads:

 

The minimum number of threads always kept running. If not specified, the default of is used.

我简单理解就是:

maxThreads:Tomcat线程池最多能起的线程数;

maxConnections:Tomcat最多能并发处理的请求(连接);

acceptCount:Tomcat维护最大的对列数;

minSpareThreads:Tomcat初始化的线程池大小或者说Tomcat线程池最少会有这么多线程。

比较容易弄混的是maxThreads和maxConnections这两个参数:

maxThreads是指Tomcat线程池做多能起的线程数,而 maxConnections 则是Tomcat一瞬间做多能够处理的并发连接数。比如maxThreads=1000,maxConnections=800,假设某一瞬间的并发时1000,那么最终Tomcat的线程数将会是800,即同时处理800个请求,剩余200进入队列“排队”,如果acceptCount=100,那么有100个请求会被拒掉。

注意:根据前面所说,只是并发那一瞬间Tomcat会起800个线程处理请求,但是稳定后,某一瞬间可能只有很少的线程处于RUNNABLE状态,大部分线程是TIMED_WAITING,如果你的应用处理时间够快的话。 所以真正决定Tomcat最大可能达到的线程数是maxConnections这个参数和并发数,当并发数超过这个参数则请求会排队,这时响应的快慢就看你的程序性能了。

以上的结论都是我个人验证和总结,如有不对,跪求指正!!!

==========================更新(2015-1-20)===========================

以上的得出结论有严重的问题,特此更正下,如果误导了某些同学十分抱歉。

主要错误的结论是:

  • Tomcat不会主动对线程池进行收缩,除非确定没有任何请求的时候,Tomcat才会将线程池收缩到minSpareThreads设置的大小;
  • Tomcat6之前的版本有一个maxSpareThreads参数,但是在7中已经移除了,所以只要前面哪怕只有一个请求,Tomcat也不会释放多于空闲的线程。

Tomcat会停止长时间闲置的线程。 Tomcat还有一个参数叫 maxIdleTime 

 

(int) The number of milliseconds before an idle thread shutsdown, unless the number of active threads are less or equal to minSpareThreads. Default value is  (1 minute)

其实从这个参数解释也能看出来Tomcat会停止闲置了超过一定时间的线程的,这个时间就是maxIdleTime。但我之前的测试中确实没有发现线程释放的现象,这是为什么呢?我发现除了这个参数线程池线程是否释放?释放多少?还跟当前Tomcat每秒处理的请求数(从Jmeter或LoadRunner来看可以理解为TPS)有关系。通过下表可以清晰的看出来 线程数 , TPS 和 maxIdleTime 之间的关系:

TPS maxIdleTime(ms) Thread Count
10 60,000 600
5 60,000 300
1 60,000 60

依次类推,当然 Thread Count 这一列是一个大约数,上下相差几个,但基本符合这样一个规则:

 

Thread Count = ( TPS *  maxIdleTime)/1000

当然这个 Thread Count 不会小于 minSpareThreads ,这个跟之前的结论还是一样的。我现在大胆猜测下(回头看源码验证下,或者哪位同学知道告诉我下,谢谢):

Tomcat线程池每次从队列头部取线程去处理请求,请求完结束后再放到队列尾部,也就是说前后两次请求处理不会用同一个线程。某个线程闲置超过maxIdleTime就释放掉。

假设首先线程池在高峰时期暴涨到1000,高峰过后Tomcat处理一次请求需要1s(从Jmeter看TPS大约就为1),那么在maxIdleTime默认的60s内会用到线程池中60个线程,那么最后理论上线程池会收缩到60(假设minSpareThreads大于60)。 另外:这个跟用不用Keep-Alive没关系(之前测试结论是因为用了Keep-Alive导致程序性能下降,TPS降低了很多导致的)

这就是为什么我之前的测试中、还有我们生产环境中线程数只增不减的原因,因为就算峰值过后我们的业务每秒请求次数仍然有100多,100*60=6000,也就是3000个线程每个线程在被回收之前肯定会被重用。

那么现在有另外一个问题,那么正常情况下为什么每秒100次的请求不会导致线程数暴增呢?也就是说线程暴增到3000的瓶颈到底在哪?这个我上面的结论其实也不是很准确。

真正决定Tomcat最大可能达到的线程数是maxConnections这个参数和并发数,当并发数超过这个参数则请求会排队,这时响应的快慢就看你的程序性能了。

这里没说清楚的是并发的概念,不管什么并发肯定是有一个时间单位的(一般是1s),准确的来讲应该是当时Tomcat处理一个请求的时间内并发数,比如当时Tomcat处理某一个请求花费了1s,那么如果这1s过来的请求数达到了3000,那么Tomcat的线程数就会为3000,maxConnections只是Tomcat做的一个限制。

欢迎斧正!

补充:

使用Jmeter可以很容易的控制请求的频率。

 

分享到:
评论

相关推荐

    tomcat-redis-session-manager-1.2-tomcat-7-java-7

    标题中的"tomcat-redis-session-manager-1.2-tomcat-7-java-7"表明这是一个针对Tomcat服务器,用于管理session的组件,版本为1.2,适配Tomcat 7和Java 7环境。这个组件的主要作用是将Tomcat的会话管理功能与Redis...

    tomcat-redis-session-manager的jar包-包含Tomcat7和Tomcat8

    总结起来,Tomcat-Redis-Session-Manager通过整合Redis和Tomcat,实现了高效、可靠的session管理,特别适用于需要处理高并发和分布式部署的Web应用。其提供的jar包简化了集成过程,让开发者能快速地在Tomcat7和...

    tomcat-native-1.1.32-win32-bin.zip

    总的来说,Tomcat Native是一个关键的组件,能够优化Apache Tomcat在Windows环境下的表现,尤其对于需要处理高负载和大量并发请求的Web应用程序来说,它的价值不容忽视。正确配置和使用Tomcat Native能够显著提升...

    apache-tomcat-9.0.10-windows-x64

    9. **性能优化**:为了提高性能,可以调整`conf/server.xml`中的各种参数,如最大线程数、最小空闲线程数等。还可以启用NIO(非阻塞I/O)或APR(Apache Portable Runtime)连接器,以提升并发处理能力。 10. **监控...

    apache-tomcat-8.0.47-src.zip

    - **线程模型**:了解Tomcat如何处理并发请求,如何使用工作线程池优化性能。 - **安全管理**:通过源码学习如何配置和实现角色、权限以及访问控制。 - **部署和热更新**:Tomcat如何监测`webapps`目录下的变化,并...

    apache-tomcat-7.0.62-windows-x64.zip

    - 根据系统资源调整Tomcat的配置,如最大线程数、内存分配等,可以提升服务器的性能和稳定性。 - 使用JVM的性能监视和调优工具(如JVisualVM)可以帮助分析和优化Tomcat的运行效率。 综上所述,Apache Tomcat ...

    jakarta-tomcat-connectors-1.2.15-src.zip

    5. **学习Java NIO和多线程**:Tomcat的连接器使用了Java的非阻塞I/O(NIO)技术,通过阅读源码,可以加深对Java并发编程的理解。 总的来说,"jakarta-tomcat-connectors-1.2.15-src.zip"为开发者提供了一个宝贵的...

    apache-tomcat-7.0.85-windows-x64

    - **启动与停止**:使用bin目录下的startup.bat和shutdown.bat脚本来启动和关闭Tomcat服务器。 - **配置server.xml**:修改conf/server.xml文件,可以设置端口、应用上下文路径等。 **4. 应用部署** - **WAR文件...

    apache-tomcat-8.5.32-windows-x64安装版

    - 调整`conf/server.xml`中的连接器配置,如增大最大线程数,提高最大请求队列长度等,以应对高并发场景。 - 使用JVM的调优工具,如JVisualVM,进行内存和CPU性能监控。 10. **与其他Java EE组件的集成**: - ...

    tomcat-connectors-1.2.40-src.zip

    Tomcat Connectors的配置可以通过`server.xml`文件进行,包括设置端口、最大连接数、超时时间、是否启用压缩等。这些配置直接影响Tomcat的性能和稳定性。 五、源码学习价值 1. 性能优化:通过阅读源码,我们可以...

    apache-tomcat-6.0.53-x86.rar

    4. **线程模型**:Tomcat使用多线程处理并发请求,提高了服务器性能。 5. **安全管理**:支持角色基础的访问控制(RBAC)和SSL加密,确保应用的安全运行。 **配置** Tomcat的配置主要通过修改`conf`目录下的配置...

    apache-tomcat-10.0.0-M7-windows-x64.zip

    - 调整`server.xml`中的连接器配置,例如增大最大线程数,以应对高并发场景。 - 使用JVM的内存调优参数,例如通过`setenv.bat`脚本设置`JAVA_OPTS`,以优化Tomcat的内存使用。 8. **监控与管理**: - Tomcat内置...

    tomcat-coyote-7.0.34-sources.jar.zip

    例如`org.apache.tomcat.util.threads.TaskQueue`和`org.apache.tomcat.util.threads.TaskThreadLocal`,它们负责任务的分配和线程上下文的维护,确保了高效的并发处理。 5. **Buffers**:`org.apache.tomcat.util....

    apache-tomcat-7.0.94-windows-x64.rar

    8. **日志与调试**: Tomcat的日志信息默认位于logs目录下,通过调整logging.properties文件,可以定制日志级别和输出方式,便于调试和监控。 9. **性能优化**: 包括调整连接器配置(Connector),如最大线程数、...

    apache-tomcat-6.0.32-windows-x64

    7. **性能优化**:Tomcat 6.0.32 版本针对64位系统进行了优化,能够处理更大的内存分配,提高并发性能,并支持多线程处理,使其在处理高流量网站时表现出色。 8. **社区支持**:Apache Tomcat有一个活跃的开发者和...

    apache-tomcat-8.0.52-windows-x86.zip

    6. **多线程模型**:改进了线程池管理,提高了并发性能和资源利用率。 三、安装与部署 1. **解压安装**:下载的"apache-tomcat-8.0.52-windows-x86.zip"文件解压后,可以直接运行bin目录下的startup.bat启动Tomcat...

    apache-tomcat-7.0.52-windows-x64

    6. **性能调优**:根据应用的需求,你可以调整Tomcat的配置参数,如最大线程数、堆内存大小、连接器设置等,以实现最佳性能。这些参数通常在`conf/server.xml`文件中配置。 7. **日志和监控**:理解和分析`logs`...

    apache-tomcat-8.0.50-windows-x64

    Apache Tomcat 8.0.50 是一个广泛使用的开源软件,它是一个符合Java Servlet和JavaServer Pages(JSP)规范的应用服务器,专门用于运行基于Java的Web应用程序。这款64位版本是针对Windows操作系统设计的,旨在充分...

    apache-tomcat-7.0.68-windows-i64

    8. **多线程支持**: Tomcat 7.0.68利用Java的多线程模型来处理并发请求,提高服务器的并发处理能力。 9. **目录结构**: 包含`conf`(配置文件)、`webapps`(放置Web应用)、`logs`(日志文件)、`temp`(临时文件...

    官方原版apache-tomcat-8.5.53-windows-x64.zip

    10. **多线程支持**:Tomcat使用多线程模型来处理并发请求,通过调整`maxThreads`和`minSpareThreads`等配置参数,可以优化服务器对并发请求的响应能力。 11. **性能优化**:除了配置参数调整外,还可以通过开启NIO...

Global site tag (gtag.js) - Google Analytics