详解Tomcat高效性能优化策略
一丶原理
概要
Tomcat大致分为两个部分,Connector组件及Container组件。Connector组件负责控制入口连接,并关联着一个Executor。Container负责Servlet容器的实现,Executor负责具体的业务逻辑,如Servlet的执行。一个请求到达服务器后,经过以下关键几步,参见图1:
图一
OS与客户端握手并建立连接,并将建立的连接放入完成队列,不妨叫Acceptor Queque。这个队列的长度就是Connector的acceptCount值。
Tomcat中的acceptor线程,不断从Acceptor Queque中获取连接。
Acceptor Queque队列中没有连接,Acceptor线程继续监视
Acceptor Queque队列中有新连接,Acceptor线程将检查当前的连接数是否超过了maxConnections
如果超过maxConnections,则阻塞。直到连接数小于maxConnections,acceptor线程将请求交由Executor负责执行。
Executor将分配worker线程来处理请求数据的读取,处理(servlet的执行)以及响应。
参数
acceptCount
acceptCount 实际上是Bind Socket时候传递的backlog值,在linux平台下含义是已经建立连接还没有被应用获取的连接队列最大长度。此时,如果请求个数达到了acceptCount,新进的请求将抛出refuse connection.
maxConnections:
顾名思义,即Tomcat允许的同时存在的最大连接数。默认BIO模式下,这个数值等于maxThreads,即最大线程数。超过这个值后,acceptor线程被Block。新进入的连接将由acceptCount控制。当当前连接数小于这个数值时,acceptor线程被唤醒,新的连接才能进入Executor执行。
executor
JDK缺省的ThreadPoolExecutor的逻辑是:
如果线程数小于coreThreadSize, 优先建立新线程。
如果线程数大于coreThreadSize小于maxThreadSize,将请求入队列等待。
队列满的时候,才扩充线程数直到maxThreadSize.
当队列满,同时线程数大于maxThreadSize时,抛出异常。
Tomcat对JDK的ThreadPoolExecutor默认行为做了修改。使用继承自×××队列的LinkedBlockingQueue的TaskQueue,并改写了其入队策略,是的tomcat的Executor具有以下特性。
只要线程不足,并且小于maxThreads, 优先建立新线程。而不是超过coreSize,先入队列。
Executor如果发生reject,则将任务继续加入队列。而默认队列的size是Integer.MAX_VALUE,因此不会Reject.
在配置中,Executor可以单独配置,并被整个Tomcat共享。如果不配置,则Connector组件将使用自己默认的实现。
maxThreads
Worker线程的最大值。每当一个请求进来时,如果当前线程数小于这个值,则创建新的线程。如果大于这个值,则查找空闲线程。如果没有空闲线程,则被Block住。
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:
图二
300个线程,只能达到140左右的吞吐量,tomcat worker线程只有17个上下,平均响应时间已经2s多了(理论上正常平均应该1.5s),继续增加线程,返回异常,说明已经达到了极限值。可以理解为,在异步情况下,20个worker线程每秒就能处理140个请求。
maxConnection的应用
为了将worker线程打满,同时对后端的异步服务有足够的信心,逐步将maxConnection调整到2000,使用2000个并发打压。此时200个worker线程都工作了。吞吐量已经达到了1308(如图3),此时应该是应用最大吞吐量了。至此,初步达到了预期效果。
图三
NIO Connector的应用
还有个NIO Connector,看到名字就是支持异步的IO了,在其它参数不变的情况下,换成NIO Connector。如图4所示,在吞吐量基本不变的情况下,线程数基本减少了一半。
图四
在启用NIO Connector,servlet及后端调用不异步的话,如图所示5,显然吞吐量上不去,而且还有所下降。
图五
图六
为了充分发挥tomcat的潜能,需要综合评估这几个重要参数。当worker线程处理足够快的时候,可以适当提高maxConnections值,以便更多的请求得到处理。
反之,如果后端线程处理较慢,则可考虑减少maxConnections及QueueSize,避免请求堆积而造成请求超时。另外NIO能更高效处理网络连接及请求。在全栈异步的情况下,能有效减少Worker线程数。
推荐阅读
-
828 华为云论文|华为云 X 实例 CPU 性能测试与优化策略
-
优化Tomcat 9设置与提升性能的技巧
-
优化Tomcat 9性能的方法
-
提升Tomcat性能的四大策略:内存管理、并发处理、缓存优化
-
优化Tomcat 9.0.4的性能
-
【摩尔线程+Colossal-AI强强联手】MusaBert登上CLUE榜单TOP10:技术细节揭秘 - 技术实力:摩尔线程凭借"软硬兼备"的技术底蕴,让MusaBert得以从底层优化到顶层。其内置多功能GPU配备AI加速和并行计算模块,提供了全面的AI与科学计算支持,为AI推理和低资源条件下的大模型训练等场景带来了高效、经济且环保的算力。 - 算法层面亮点:依托Colossal-AI AI大模型开发系统,MusaBert在训练过程中展现出了卓越的并行性能与易用性,特别在预处理阶段对DataLoader进行了优化,适应低资源环境高效处理海量数据。同时,通过精细的建模优化、领域内数据增强以及Adan优化器等手段,挖掘和展示了预训练语言模型出色的语义理解潜力。基于MusaBert,摩尔线程自主研发的MusaSim通过对比学习方法微调,结合百万对标注数据,MusaSim在多个任务如语义相似度、意图识别和情绪分析中均表现出色。 - 数据资源丰富:MusaBert除了自家高质量语义相似数据外,还融合了悟道开源200GB数据、CLUE社区80GB数据,以及浪潮公司提供的1TB高质量数据,保证模型即便在较小规模下仍具备良好性能。 当前,MusaBert已成功应用于摩尔线程的智能客服与数字人项目,并广泛服务于语义相似度、情绪识别、阅读理解与声韵识别等领域。为了降低大模型开发和应用难度,MusaBert及其相关高质量模型代码已在Colossal-AI仓库开源,可快速训练优质中文BERT模型。同时,通过摩尔线程与潞晨科技的深度合作,仅需一张多功能GPU单卡便能高效训练MusaBert或更大规模的GPT2模型,显著降低预训练成本,进一步推动双方在低资源大模型训练领域的共享目标。 MusaBert荣登CLUE榜单TOP10,象征着摩尔线程与潞晨科技联合研发团队在中文预训练研究领域的领先地位。展望未来,双方将携手探索更大规模的自然语言模型研究,充分运用上游数据资源,产出更为强大的模型并开源。持续强化在摩尔线程多功能GPU上的大模型训练能力,特别是在消费级显卡等低资源环境下,致力于降低使用大模型训练的门槛与成本,推动人工智能更加普惠。而潞晨科技作为重要合作伙伴,将继续发挥关键作用。
-
解决由压力测试导致的系统性能问题:优化进程详解
-
如何有效地运用CPU管理策略来优化容器性能指南
-
iOS性能提升实战2:加载大图列表的优化策略
-
前端性能提升:详细解析图片优化策略