生产环境Tomcat的优化
估计不少公司的生产环境都使用Tomcat使用为Web容器,估计也有不少同窗已经对生产环境的Tomcat的进行各类各样优化,努力找出Tomcat的性能瓶颈,在生产环境提供一个稳定的、高效的Tomcat。让在咱们在生产环境进行优化以前,先对Tomcat进行压力测试,找出Tomcat性能瓶颈,而后修改优化配置,再观察Tomcat是否知足咱们的性能要求。html
测试环境

测试系统图示:算法

测试系统环境说明:数据库
- 部署Tomcat的服务器程序是一个WAR包,WAR只有一个index.jsp页面,jsp页面的body只有一行” <p>hello world!</p>”,除此之外War中再没有任何额外的Jar类和Lib包。
- Agent客户端向服务器发送Http请求。
- Agent会把请求结果反馈至nGrinder管理机,包括TPS峰值、MTT、TPS图表等一系列数据。
- Tomcat服务器已接入Pinpoint,Pinpoint能够查询Tomcat服务器的heap、jvm cpu的使用状况。
- Tomcat的Webapps除测试包test.war外,仍是host-manager、manager、ROOT均有Tomcat自带,方便偶尔观察Tomcat的线程池状况。
- Pinpoint、Tomcat监控严格意义来讲会影响测试结果,但本次咱们测试中忽略不计。
测试结果
heap.size=2G&G1


大约在Agent启动17分钟后,Tomcat的JVM开始出现Full GC(红色框是本次GC图),而且出现长时间STW事件。从第一个Full GC开始后,并且Full GC越来密集,大部分的停顿时间都在10000ms以上,对一个Web服务器来讲停顿10秒钟是不可接受的。apache
从吞吐量的角度来看,系统启动17分钟之后吞吐量也下来了,致使Agent以出错的方式结束。缓存
heap.size=4G&G1


从GC图来看,能够看到存在比较频繁的Full GC,并且Full GC的持续时间较长为10741ms,即系统有10.7S停顿,系统的响应延迟现象是很明显的。tomcat
从测试结果来看,能够看到TPS的图在前面20多分钟属于相对稳定,可是以后就频繁出现为TPS为0或接近0的状况,结合上图能够知道此时系统正在进行Full GC。从整个测试结果的数值上看,TPS的平均值为4,065.2,TPS峰值为5,251.5,测试的后期TPS波动较大。服务器
heap.size=4G&G1(6客户端)


从GC图上看本次的测试没有发生Full GC事件,系统延迟也比较小,可是不能简单认为此次的测试的GC活动对系统没有影响。网络
从测试结果图来看,TPS的趋势整体平衡,波动较小,没有出现特别低的点,整体属于优秀。其中,TPS的平均值为2,771.7,峰值为3,284,数据属于比较理想的状况。session
heap.size=6G&G1


从GC图上看本次的测试没有发生Full GC事件,能够简单认为由于GC事件对系统延迟比较小,可是不能简单认为此次的测试的GC活动对系统没有影响。并发
从测试结果图来看,TPS的趋势整体平衡,但也存在波动的状况,特别是有二次比较低的点,分别是300TPS和700TPS,整体属于良好。其中,TPS的平均值为4,095.7,峰值为5,412.5,数据属于比较理想的状况。
heap.size=6G&ParallelOld


从GC图能够看到堆的大小几乎是沿着45度的角度增加,而且在系统测试的34分钟后,出现第一次Full GC,并且此次的Full GC是持续时间为86569ms,即86.6S。也便是由于Full GC,系统中止响应长达86.6S,直接致使Agent认为服务器没有响应从而中止发送请求,从而结束了测试。
从Agent的统计图表来看,大约在开启压力测试22分钟后,TPS的波动很是大,TPS有时甚至为零。另外此处的TPS的平均值为3,836.1,TPS峰值为4,554.5,能够对比下” heap.size=6G&G1”的对应数值(TPS 4,095.7和TPS峰值为5,412.5)均有降低。能够看出来,ParallelOld算法在停顿方面的缺点仍是很明显的,特别是大HeapSize的状况下。
heap.size=4G&ParallelOld(6客户端)


GC图上面来看,已经出现密集的Full GC,时间比较长的为26971ms,即27秒。
测试结果来看,29以后的TPS很不稳定,存在比较密集的触零点,属于比较差的测试结果。
heap.size=4G&CMS


从上面的GC图能够看到Full GC的事件很频繁,其中有一个持续46651ms,并且底部的短期Full GC很频繁,停顿时间较长。
从测试结果来看,大概在开始测试的29分钟时后TPS波动很大,甚至有的接近底部。从TPS的走势图来看,表现为是不稳定。测试过程当中平均TPS为4060.2,峰值为4855。
heap.size=4G&CMS(6客户端)


从GC图上看本次的测试发生6次Full GC事件,其中一次最长时间的一次Full GC事件为174ms,即0.1s,系统没有由于Full GC 而产生时间的停顿和延迟。
从测试结果图来看,TPS的趋势整体平衡,波动较小,没有出现特别低的点,整体属于优秀。其中,TPS的平均值为2,841.7,峰值为3,658.5,数据属于比较理想的状况。
heap.size=6G&CMS


从上面的GC图能够看到发生好几回的Full GC事件,但每次的Full持续的时间不长,其中一次是67ms,这是咱们能够接受的停顿时间,时间间隔比较是大概7分钟发生一次Full GC,但时间均小于100ms。
从上面的测试结果图表来看,TPS的量一直比较稳定,没有浮动很大的状况。TPS的平均值为4,238.8,峰值为5,346,其中峰值属于以上测试状况较好的,比” heap.size=6G&G1”的峰值5,412.5略小一点,但TPS” heap.size=6G&G1”的平均值4,095.7比更大。
测试结果分析与总结:
- Tomcat8默认使用NIO做为链接处理器,NIO相对的阻塞式BIO模式来讲,NIO处理链接更高效、更节省线程,但NIO还不是Tomcat的链接器的最佳选择,APR才是。由于APR是从操做系统级别解决IO问题,Tomcat经过JNI的方式调用系统资源,提升服务器的并发处理性能,可是APR须要额外安装和配置。具体能够参考:https://tomcat.apache.org/tomcat-8.5-doc/apr.html
- 关闭Tomcat的” AccessLog”的输出(即注释:conf/server.xml的Valve className="org.apache.catalina.valves.AccessLogValve”节点),由于AccessLog平时使用很少,关闭后能够减小Tomcat的IO操做。这个操做须要自身业务系统的状况判断,确实不须要才关掉此日志输出。
- Tomcat sessionid生成器致使的延迟,这个问题不是每次测试都会出现,可是一旦出现会致使Tomcat出现相似假死的现象。具体表现为”org.apache.catalina.util.SessionIdGeneratorBase.createSecureRandom Creation of SecureRandom instance for session ID generation using [SHA1PRNG] took [3,533] milliseconds”。能够参考:https://stackoverflow.com/questions/28201794/slow-startup-on-Tomcat-7-0-57-because-of-securerandom
- “too many open files”现象,当Tomcat并发的链接数太多时,即Tomcat进程的句柄数超出系统限制,就会出现” too many open files”,这个时候须要修改操做系统的默认参数。具体能够参考:https://stackoverflow.com/questions/36863451/apache-tomcat-exception-too-many-open-files?rq=1
- Tomcat 线程池配置,在conf/server.xml文件中,” Connector”配置节点能够修改Tomcat的线程池大小和链接等参数。
- 网上不少帖子说一个Tomcat进程最多只能配置1000线程,可是我在实际的测试过程配置了1005线程,而且经过Tomcat自带的监控观察到Tomcatpool大小为1005,系统能够正常运行,因此只能配置1000线程的说法是错误。
- 基于个人环境测试,Tomcat的thread pool的大小为300时属于比较合理。由于这个时候的MTT(系统平均响应时间)属于较小,而且TPS值较高而且持续较长时间运行,TPS平均值也没有出现较大波动。
- 基于个人环境测试,Tomcat的thread pool的大于400时,在测试的初期时,TPS的平均水平和峰值均可以获取比较高水平。但MTT时间增长了,即系统的响应时间增长了,thread pool并非单纯的增大绝对数值就能够增长系统并发处理能力,而且在系统后期TPS回落较大,系统的可能出现假死、内存溢出等事件。因此Tomcat的线程池大小务必要根据硬件和内存等配置来设置。
- Tomcat的配置JVM。
- Heap大小及回收算法的优化配置,这个优化项是很是重要的。Tomcat8的JVM默认参数为:” CommandLine flags: -XX:InitialHeapSize=128950016 -XX:MaxHeapSize=2063200256 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseParallelGC”。垃圾收集的算法为:ParallelGC,MaxHeapSize(堆的容量)约1.92G,能够看到JVM是很保守的参数启动的。这一点能够理解,由于JVM为了兼容绝大部分的硬件和环境而致使的,因此在硬件或要求高的状况下,优化是必须的。
- 由于操做系统有8G内存,除掉操做系统和一些必要的程序,Java进程可使用的内存超过6G。为了提升Tomcat的吞吐量和减小延迟,在ParallelOld、CMS、G1三种算法选择一种。” Xms6144m Xmx6144m G1”组合通过8小时没有发生Full GC,并且在TPS线也很稳定,MTT也表现优秀。另一个组合” Xms6144m Xmx6144m CMS”表现优秀,即CMS垃圾回收算法。自从有了G1,不少人都以为只有G1都是才是最好的,其实,CMS也是一种优秀收集算法,特别是4G、6G级别的堆内存,差异其实不大,甚至在MTT方面表现更优秀。或者说有JDK1.8以前CMS更优秀,由于以前的G1并非很成熟,即便是JDK1.8,G1在等预测模型方面有待改进。在测试过程当中的同等条件下,CMS的TPS平均值、TPS峰值、MTT值均比G1的有更好的表现,因此说CMS表现很是优秀的。
- ParallelOld在堆较大时,缺点是很明显的,就是当年老代进行回收,停顿时间太长,由于ParallelOld进行一次年老代标记、清理时耗时太长了,会致使长时间STW事件,系统出现停顿、响应超时。
优化总结
Tomcat的最大并发数,这个估计是很想人知道数值。能够简单的认为,Tomcat在同一时刻能处理请求数(假设请求同时到达),那么Tomcat的并发数是Tomcat线程池的大小。我的认为:TPS的平均值、峰值对咱们的生产环境更有意义,TPS峰值= ThreadPool/MTT。更重要的是,Tomcat很容易达到比较高的TPS峰值,可是要想在峰值保持长时间稳定运行是很是困难的,由于峰值TPS时,堆中分配内存的速度会远远大于堆内存回收的速度,系统运行一段时间后,系统就会出现Full GC,系统的Full GC就会愈来愈频繁,停顿时间会愈来愈时,MTT也会愈来愈长,请求可能出现超时、报错,从而TPS的出现直线下跌现象,若是请求的数仍然保持较高的数值,系统就会现出崩溃OutOfMemoryError现象,以下图。

因此短期内Tomcat的TPS峰值是能够达到比较高数值,具体能够根据: ThreadPool /MTT计算,但须要长时间稳定运行,必需要考虑HeapSize、CPU处理能力、MTT、网络、IO等综合因素考虑,而后进行压力测试,找出性能的拐点。
虽然能够经过配置进行Tomcat优化后,相对原始的Tomcat优化后有着较高提高的空间,特别是HeapSize、GC收集算法、 ThreadPool的优化配置。可是咱们系统的性能瓶颈每每不是由Tomcat致使,更多的是咱们的系统在处理业务逻辑过程存在的问题致使的,如:频繁的数据库操做、不合理的使用索引、没有高命中率的缓存、过多的IO操做等操做,这些操做对系统的性能影响更大,优化后能够得到更大的更高性能提高。因此咱们的优化顺序应该是:先优化业务逻辑实现,而后再考虑优化Tomcat,而后再优化业务逻辑实现。由于业务逻辑实现的优化空间更大,更值得咱们花费时间优化。