java—Tomcat高性能调优方案详解

来源:互联网 发布:u盘检测软件 编辑:程序博客网 时间:2024/06/07 23:59
java飞虎队 2017-10-06 10:04

一丶原理

概要

Tomcat大致分为两个部分,Connector组件及Container组件。

Connector组件负责控制入口连接,并关联着一个Executor。

Container负责Servlet容器的实现,Executor负责具体的业务逻辑,如Servlet的执行。一个请求到达服务器后,经过以下关键几步,参见图1:

java—Tomcat高性能调优方案详解

图一

  1. OS与客户端握手并建立连接,并将建立的连接放入完成队列,不妨叫Acceptor Queque。这个队列的长度就是Connector的acceptCount值。

  2. Tomcat中的acceptor线程,不断从Acceptor Queque中获取连接。

  3. Acceptor Queque队列中没有连接,Acceptor线程继续监视

  4. Acceptor Queque队列中有新连接,Acceptor线程将检查当前的连接数是否超过了maxConnections

  5. 如果超过maxConnections,则阻塞。直到连接数小于maxConnections,acceptor线程将请求交由Executor负责执行。

  6. Executor将分配worker线程来处理请求数据的读取,处理(servlet的执行)以及响应。

参数

  1. acceptCount

    acceptCount 实际上是Bind Socket时候传递的backlog值,在linux平台下含义是已经建立连接还没有被应用获取的连接队列最大长度。此时,如果请求个数达到了acceptCount,新进的请求将抛出refuse connection.

  2. maxConnections:

    顾名思义,即Tomcat允许的同时存在的最大连接数。默认BIO模式下,这个数值等于maxThreads,即最大线程数。超过这个值后,acceptor线程被Block。新进入的连接将由acceptCount控制。当当前连接数小于这个数值时,acceptor线程被唤醒,新的连接才能进入Executor执行。

  3. executor

    JDK缺省的ThreadPoolExecutor的逻辑是:

  1. 如果线程数小于coreThreadSize, 优先建立新线程。

  2. 如果线程数大于coreThreadSize小于maxThreadSize,将请求入队列等待。

  3. 队列满的时候,才扩充线程数直到maxThreadSize.

  4. 当队列满,同时线程数大于maxThreadSize时,抛出异常。

    Tomcat对JDK的ThreadPoolExecutor默认行为做了修改。使用继承自无界队列的LinkedBlockingQueue的TaskQueue,并改写了其入队策略,是的tomcat的Executor具有以下特性:

  5.    4.1 只要线程不足,并且小于maxThreads, 优先建立新线程。而不是超过coreSize,先入队列。

  6.    4.2 Executor如果发生reject,则将任务继续加入队列。而默认队列的size是Integer.MAX_VALUE,因此不会Reject.
  7. 在配置中,Executor可以单独配置,并被整个Tomcat共享。如果不配置,则Connector组件将使用自己默认的实现。

  8. maxThreads

    Worker线程的最大值。每当一个请求进来时,如果当前线程数小于这个值,则创建新的线程。如果大于这个值,则查找空闲线程。如果没有空闲线程,则被Block住。

  9. protocol

    可以有三个取值,BIO,NIO, APR。BIO使用阻塞Socket实现,一个连接一个线程的模型。NIO使用的时候非阻塞socket实现。APR是apache实现的一个夸平台的库,也是apache http服务以来的核心网络组件。

以上介绍的几个参数之间存在着微妙的关系。一方面可以限制过多的请求来保护系统资源,另一方面提供缓冲队列来提高系统的吞吐量。在低并发条件下,默认值基本满足要求。而在高并发的情况下,就需要根据具体的计算资源,评估以上参数的设置,来充分调动计算资源。

二丶应用

默认值的效果

背景中的case除了使用异步Servlet,后端依赖的服务也全部采用异步调用。即在tomcat的woker线程中,没有任何阻塞,只是做纯粹的本地CPU计算, 为了模拟服务失败的情况,后端服务被mock住,并随机sleep 0~3s。初次使用了tomcat的默认设置(具体值参见后表中第1条)。由于后端woker线程很快(发送异步请求后就结束了),一个线程在1s内可以处理多于一个的请求。此时,如果maxConnections等于maxThreads 的值,很显然不能完全激活全部工作线程。如图2:

java—Tomcat高性能调优方案详解

图二

300个线程,只能达到140左右的吞吐量,tomcat worker线程只有17个上下,平均响应时间已经2s多了(理论上正常平均应该1.5s),继续增加线程,返回异常,说明已经达到了极限值。可以理解为,在异步情况下,20个worker线程每秒就能处理140个请求。

maxConnection的应用

为了将worker线程打满,同时对后端的异步服务有足够的信心,逐步将maxConnection调整到2000,使用2000个并发打压。此时200个worker线程都工作了。吞吐量已经达到了1308(如图3),此时应该是应用最大吞吐量了。至此,初步达到了预期效果。

java—Tomcat高性能调优方案详解

图三

NIO Connector的应用

还有个NIO Connector,看到名字就是支持异步的IO了,在其它参数不变的情况下,换成NIO Connector。如图4所示,在吞吐量基本不变的情况下,线程数基本减少了一半。

java—Tomcat高性能调优方案详解

图四

在启用NIO Connector,servlet及后端调用不异步的话,如图所示5,显然吞吐量上不去,而且还有所下降。

java—Tomcat高性能调优方案详解

图五

java—Tomcat高性能调优方案详解

图六

为了充分发挥tomcat的潜能,需要综合评估这几个重要参数。当worker线程处理足够快的时候,可以适当提高maxConnections值,以便更多的请求得到处理。

反之,如果后端线程处理较慢,则可考虑减少maxConnections及QueueSize,避免请求堆积而造成请求超时。另外NIO能更高效处理网络连接及请求。在全栈异步的情况下,能有效减少Worker线程数。

总结

好了到这里, java—Tomcat高性能调优方案详解就结束了,,不足之处还望大家多多包涵!!觉得收获的话可以点个关注收藏转发一波喔,谢谢大佬们支持。(吹一波,233~~)

下面和大家交流几点编程的经验:

1、多写多敲代码,好的代码与扎实的基础知识一定是实践出来的

2丶 测试、测试再测试,如果你不彻底测试自己的代码,那恐怕你开发的就不只是代码,可能还会声名狼藉。

3丶 简化算法,代码如恶魔,在你完成编码后,应回头并且优化它。从长远来看,这里或那里一些的改进,会让后来的支持人员更加轻松。

4、可以去腾讯课堂的图灵学院学习一下java架构实战案例,还挺不错的。

最后,每一位读到这里的网友,感谢你们能耐心地看完。希望在成为一名更优秀的Java程序员的道路上,我们可以一起学习、一起进步。

java—Tomcat高性能调优方案详解