如何对Tomcat进行性能优化?
对于提供接口服务的应用程序来说,很多都使用SpringBoot默认的Servlet容器Tomcat。
上线之初,由于大部分流量较小,我们不会对Tomcat进行特殊的参数调整。
然而,随着流量的增加,应用的性能指标变得越来越差。这个时候我们大多数人都会选择扩容。除了扩容之外,我们还可以选择对Tomcat进行性能调优,在不增加成本的情况下提升性能。
今天我们将分享如何对Tomcat进行简单的性能调优,以提高应用程序的性能。
从上图可以看出,Tomcat将其业务抽象为Server, Service, Connector, ContAIner等组件,每个组件都有不同的作用。
Server组件是Tomcat的最外层组件,它是Tomcat实例本身的抽象,代表Tomcat本身。一个服务器组件可以有一个或多个服务组件。
Service组件是Tomcat中提供服务和处理请求的一组组件。一个服务组件可以有多个连接器和一个容器。多个Connector表示它可以使用多种协议同时接收用户请求。
连接器负责处理客户端连接,它提供对各种服务协议的支持,包括BIO、NIO、AIO等,它存在的价值在于屏蔽多协议Container的复杂性,统一Container的处理标准。
Container组件是负责具体业务逻辑处理的容器。当Connector组件与客户端建立连接时,它会将请求转发给Container组件的Engine组件进行处理。
至此,Tomcat的核心组件就基本完成了。其实Container组件里面还有很多细分的组件。如果你对业务的抽象感兴趣的话,可以继续看下去。
可以看出,Host是虚拟主机的抽象,Context是应用程序的抽象,Wrapper是Servlet的抽象,Engine是处理层的抽象
在了解核心参数之前,我们需要对Tomcat对于请求的处理流程有一个大概的了解。
Tomcat对请求的处理流程如下。
在上面的示意图中,有三个非常关键的核心参数,这也是性能调优的关键。
从以上三个参数的含义,我们可以得知以下结论。
客户端并不直接与Tomcat的Connector组件建立联系,而是先与操作系统建立联系,然后交给Connector。
这一点非常重要,否则你将无法理解该acceptCount参数。
不仅Connector组件中有一个队列,操作系统中也有一个队列来临时存储与客户端的连接,这也是一个关键点。我们所说的线程池是指Container容器中的线程池。
理解这三个核心参数的含义非常重要,否则就没有办法进行后续的性能调优工作。
我们知道指的maxThreads是最大请求处理线程数,Tomcat7和Tomcat8都是默认的200。
该参数的设置需要根据任务的执行内容进行调整。一般来说,计算公式为:最大线程数 = ((IO time + CPU time)/CPU time) * CPU核心数。
这个公式的思想其实很简单,就是最大化的利用CPU资源。
任务的时间消耗分为IO时间消耗和CPU时间消耗。基本上IO时间消耗是最多的,此时CPU无事可做。
所以如果可以让CPU在任务等待IO的同时处理其他任务,那么CPU利用率就会提高。
一般来说,由于IO耗时远大于CPU耗时,所以maxThreads根据公式计算出来的数量会远大于CPU核数,这是正常的。
需要注意的是,这个值并不是越高越好。因为一旦线程过多,CPU就需要进行上下文切换,消耗部分CPU资源。因此,最好的办法是利用上面的公式计算出一个基准值,然后进行压力测试调整到合理的值。
一般来说,如果 的值maxThreads增大,但吞吐量没有增加或减少,则可能表明已经达到瓶颈。
maxConnections指当线程池中的线程达到最大值并且全部在忙时,Connector中的队列可以容纳的最大连接数。
一般来说,我们要设定一个合理的值,不能让它无限制地堆积。
因为Tomcat的处理能力肯定是有限的,到了一定程度就肯定处理不了了。所以,积累太多也是没有用的。反而会造成内存堆积,最终导致内存溢出OOM。
一般来说,经验值可以设置为与 maxThreads相同的大小。
这样比较合理,因为队列中的连接最多只需要等待线程处理一个任务,就不会等待太久,响应时间也不会太长。如果想缩短响应时间,可以调整maxConnections低于maxThreads,这样可以减少一些响应时间。
但需要注意的是,如果降得太低,性能可能会严重下降,吞吐量也会降低。
acceptCount指当Container线程池达到最大数量并且没有空闲线程且Connector队列达到最大数量时,操作系统可以接受的最大连接数。
当队列中的数量达到最大值时,所有传入的请求都将被拒绝。默认值为100。这可以理解为操作系统的一种自我保护机制。如果积累的太多无法处理,那就直接拒绝,以保护自己的资源。
该参数的调优资料相对较少,但根据其含义,不建议该值大于maxConnections。
因为这个队列中的连接需要等待。如果该值太大,则意味着会有很多连接没有被处理。
连接越多,等待时间越长,响应时间越慢。如果想要较短的响应时间,您可能应该降低该值。
有的同学会问,既然有了acceptCount,为什么还需要呢maxConnections?这不是重复了吗?其实在BIO中,这两个值基本是一致的。
猜测是因为后来NIO,AIO等技术的出现操作系统可以接受更多的客户端连接。
因此,操作系统可以先建立连接缓存,然后Connector就可以直接从操作系统获取连接,这样就不需要等待操作系统建立耗时的TCP连接,从而提高效率。
除了以上三个参数之外,还有几个非核心参数,但我认为还是有一定作用的。
今天我们分享了Tomcat的核心组件,然后讲解了Tomcat在处理请求时的三个核心参数以及调优经验。
对于maxThreads参数来说,如果按照公式计算,我们需要获取IO时间和CPU时间,但实际上这两个值并不容易获取。
所以一般来说,我们可以通过压力测试得到一个比较合适的maxThreads。
对于该maxConnections参数,您可以设置与maxThreads 相同的值,然后根据具体情况进行调整。如果你想减少响应时间,可以稍微调低,否则可以调高。
对于该acceptCount参数,其调优逻辑与maxConnections 类似,可以类似maxConnections 进行设置,然后根据相应的时间要求进行微调。