> 文档中心 > Tomcat性能调优-演示分析

Tomcat性能调优-演示分析

文章目录

  • 👑前言
  • ❤️Tomcat调优
    • 🤍 禁用AJP服务
    • 🤍 修改线程
    • 🤍 设置为NIO2
  • ❤️JVM优化
    • 🤍 设置不同的垃圾回收器
    • 🤍 调整最大堆内存大小
    • 🤍 调整新生代和老年代
  • 总结💯

👑前言

前面咱们对Tomcat有了一个大体的认知,对其工作流程也了解了,下面开始干实事了。咱学习tomcat最主要的目的是可以根据情况对tomcat进行专门的配置,让其发挥更优的性能,更好的为我们工作。本文就通过实际配置演示来看一下tomcat的各种优化以及优化后的效果。

❤️Tomcat调优

🤍 禁用AJP服务

AJP(Apache JServer Protocol)是定向包协议 。WEB服务器和Servlet容器通过TCP连接来交互;为了节省SOCKET创建的昂贵代价,WEB服务器会尝试维护一个永久TCP连接到servlet容器,并且在多个请求和响应周期过程会重用连接。

现在部署的tomcat都是通过Nginx+Tomcat的模式,基本很少有用到AJP的情况。可以禁用通过修改server.xml禁用到AJP服务。

<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />

关于AJP这个就不演示了,因为这没有啥可配置分析的,直接禁掉就OK了。

🤍 修改线程池

🔊演示代码
为了达到效果,咱在测试代码的servlet的doGet方法调用了下面的循环,为了让线程进入等待状态,一开始用Sleep睡眠的发现效果不好,不真实,因为没有消耗CPU,正常情况下后台的计算是要消耗CPU的,所以整了个循环计算。

 public void doMethod() {  //这是使用的new String创建新对象是为了后面JVM优化做演示      for (int i = 0; i < 10000; i++) {    String str = new String("");    String str2 = new String("");    String str3 = new String("");    String str4 = new String("");    for (int j = 0; j < 10; j++) { str = str + i + j; str2 = str.substring(0); str3 = str2.substring(1); str4 = str + j + i;    }}   }

🔊线程池配置
在tomcat中还有一个连接线程池,对于线程池大家都比较了解,咱对线程池参数进行配置查看一下。将线程池的注释打开,然后再Connector中使用线程池。

线程池配置<Executor name="tomcatThreadPool" namePrefix="catalina‐exec‐"   maxThreads="500" 最大线程数量   minSpareThreads="50" 初始线程数量   prestartminSpareThreads="true" maxQueueSize="100" 等待队列线程数量   />连接器配置<Connector port="8080" protocol="HTTP/1.1"    executor="tomcatThreadPool"    connectionTimeout="20000"    redirectPort="8443" />

🔊开始演示
打开Jemter配置10000个线程,请开始你的表演。主要关注平均响应时间、异常情况和吞吐量三列。
注意:下面只是根据演示项目做的调整,实际项目中需要自己进行测试去找最优的值
1️⃣ 第一次:最大线程数量50,初始化线程数量5。
Tomcat性能调优-演示分析
🔴最终结果平均响应时间大约3800ms左右,异常情况无,吞吐量大约220/s。这里需要说一下这个平均响应时间这么大是因为等待队列设置的100,进入等待池等待的时间也算本次请求的执行时间
2️⃣ 第二次:最大线程数量150,初始化线程数量15。
Tomcat性能调优-演示分析
🔴最终结果平均响应时间大约1400ms左右,异常情况无,吞吐量大约630/s。
3️⃣第三次:最大线程数量500,初始化线程数量50。
Tomcat性能调优-演示分析
🔴最终结果平均响应时间大约1300ms左右,异常情况无,吞吐量大约620/s。

🔊结果分析
可以看到第二次和第三次是几乎是没有区别的,可以说明线程池并不是越多越好,而是有一个极限值,超过这个值之后基本没有变化了。为什么没有变化了呢?应该是电脑的硬件限制了。咱用jconsole看一下执行过程,可以看到活动线程数量基本没有变化,说明线程已经塞满了,再看一下CPU占用率,光Tomcat占用了80%多,加上其他的软件占用的CPU已经把CPU占满了,可以打开电脑任务管理器查看CPU一直100%使用,最终结果被CPU限制了。
✨最终结果说明:Tomcat配置线程池的数量有一个最优值,超过这个值配置再多也基本无效了,再大了可能会有反向效果因为线程池的管理也需要消耗性能。
在这里插入图片描述

🤍 设置为NIO2

前面的文章中也说到了,Tomcat连接器支持BIO、NIO、NIO2和APR的方式,APR大幅提高了安装起来很费劲,所以咱这就不演示了,演示下NIO2,官方说明上NIO2速度跟它差不多,而且设置简单就直接用NIO2再运行一次上面前两次的结果看一下:

<Connector executor="tomcatThreadPool" port="8080" protocol="org.apache.coyote.http11.Http11Nio2Protocol"  设置为Http1.1协议的NIO2模式connectionTimeout="20000" redirectPort="8443" />

1️⃣ 第一次:最大线程数量50,初始化线程数量5。
最终结果平均响应时间大约3800ms左右,异常情况无,吞吐量大约220/s。
Tomcat性能调优-演示分析🔴最终结果平均响应时间大约1300ms左右,吞吐量大约590/s,大幅提高了性能。但是本次执行的结果中出现了异常结果,看一下异常原因是有的请求被拒绝了。
在这里插入图片描述

2️⃣第二次 最大线程数量150,初始化线程数量15,使用NIO2的模式
最终结果平均响应时间大约1400ms左右,异常情况无,吞吐量大约630/s。
Tomcat性能调优-演示分析
🔴最终结果平均响应时间大约1200ms左右,吞吐量大约620/s,中间有过吞吐量达到650的情况,性能差别不大。
🔊结果分析
✨最终结果说明:在不受硬件限制的情况下可以看到NIO2相较于NIO模式来说性能大幅提高了,但是在NIO2的模式下会拒绝部分请求的执行,所以NIO2模式需要看实际业务情况是否配置。

❤️JVM优化

注意:JVM优化主要优化的垃圾回收,这个只是演示效果,因为演示代码中的垃圾情况是不一定的,真实情况下可能有差异。JVM优化的代码与上面代码不同,为了演示效果创建了很多对象,请不要把两边的吞吐量做比较。

🤍 设置不同的垃圾回收器

修改tomcat的bin目录下面catalina.bat文件(本电脑是windows的,所以修改的.bat文件),可以看到文件中有个注释可以配置Java的运行环境,演示环境的其他配置:最大堆内存和初始堆内存都设置为512M,减少堆内存扩展带来的性能消耗。并且将gc日志输出到F盘logs文件夹下面。
🔊设置为并行垃圾回收器
将新生代和老年代均设置为并行垃圾回收器,当然JDK8默认的也是使用的并行垃圾收集器,但是为了效果咱还是设置一下。

set "JAVA_OPTS=%JAVA_OPTS% -XX:+UseParallelGC -XX:+UseParallelOldGC -Xms512m -Xmx512m  -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -Xloggc:F://logs/gc.log"

Tomcat性能调优-演示分析
🔴最终结果平均响应时间大约2400ms左右,吞吐量大约370/s左右。再看一下GC分析报告,GC的吞吐量为94.497。
Tomcat性能调优-演示分析
🔊设置为G1垃圾回收器
将垃圾回收器设置为G1垃圾回收器,回收最大停顿时间为100ms。

set "JAVA_OPTS=%JAVA_OPTS% -XX:+UseG1GC -XX:MaxGCPauseMillis=100 -Xms512m -Xmx512m  -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -Xloggc:F://logs/gc.log"

Tomcat性能调优-演示分析
🔴最终结果平均响应时间大约2200ms左右,吞吐量大约400/s左右。再看一下GC分析报告,GC的吞吐量为97.863。
在这里插入图片描述
🔊结果分析
✨最终结果说明:对于比较消耗内存的项目来说可以通过设置不同的垃圾回收机制来减少垃圾回收占用的时间,从而提高访问的平均时间,最终达到提高吞吐量的目的。

🤍 调整最大堆内存大小

因为演示项目是比较占用内存的,遇到这种项目也可以调整堆内存大小来减少垃圾回收时间。本次演示采用的是并行垃圾回收机制。
🔊扩大堆内存大小
扩堆内存大小配置,将初始大小和最大堆内存都设置为1024M
Tomcat性能调优-演示分析
🔴最终结果平均响应时间大约2200ms左右,吞吐量大约400/s左右。再看一下GC分析报告,GC的吞吐量为97.697。相比较使用512M堆内存的并行垃圾回收机制来说性能也有了明显的提升。
在这里插入图片描述
🔊结果分析
✨最终结果说明:对于比较消耗内存的项目来说可以通过提高堆内存大小来减少垃圾回收占用的时间,从而提高访问的平均时间,最终达到提高吞吐量的目的。

🤍 调整新生代和老年代

根据分析写的代码中主要是因为新建对象比较消耗内存,下面咱调整下新生代和老年代的比例,将新生代和老年代设置为1:1的大小,Eden:S1:S2设置为8:1:1,其实看代码也可以看出来for循环中创建的对象很多情况下不可达了,所以在新生代就给处理了。还是参照堆内存大小为512M来做实验。注意:一般优化直接调整垃圾回收机制和对内存大小就行了,这个演示只是试试玩

set "JAVA_OPTS=%JAVA_OPTS% -XX:+UseParallelGC -XX:+UseParallelOldGC -Xms512m -Xmx512m -XX:NewRatio=1 -XX:SurvivorRatio=8 -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -Xloggc:F://logs/gc.log"

Tomcat性能调优-演示分析
🔴最终结果平均响应时间大约2100ms左右,吞吐量大约400/s左右。再看一下GC分析报告,GC的吞吐量为97.093。可以看到最后的性能也是有明显提升。
在这里插入图片描述
🔊结果分析
✨最终结果说明:对于不同的项目可以根据项目的情况调整新生代和老年代的比例来提高程序的性能,但是这个调整是比较复杂的一般不建议,新生代和老年代的比例默认值是总结出来的经验,一个完整的项目肯定不像当前演示项目一样大部分全占用到了其中某部分上。所以新生代和老年代比例调整要慎用,对于那种小脚本程序可以根据情况调整,大项目就别调整新生代老年代的占比了。如果想要调整的话先进行GC分析,看看新生代和老年代中每部分使用的内存情况做一下分析再调整,胡乱调整很可能有反效果。

总结💯

Tomcat调优主要有两方面,一方面是Tomcat中连接线程池的调优,线程池调优需要根据电脑硬件情况进行多次调整查看,找到一个最优值;另一方面是对JVM的调优,Tomcat是Java语言编写的所以JVM调优适用于Tomcat,对于JVM调优着重点设置堆内存大小和垃圾回收器。根据G1垃圾回收器的原理,对于内存较大的项目来说,G1垃圾回收器效果会更好。