十.吊打面试官系列-Tomcat优化-通过压测Tomcat调优实战

前言

上一篇文章我们讲解了一下Tomcat底层的结构和执行原理,我们需要重点去掌握的是Tomcat的高内聚低耦合的设计,以及责任链模式,以及Tomcat NIO编程模式,这些是Tomcat比较核心的点,本篇文章我们将对Tomcat的参数做一些了解,然后通过Jemeter压测来对Tomcat进行调优。

一.Tomcat参数

首先我们来了解一下Tomcat可以优化的参数,官方文档上有详细的解释 :https://tomcat.apache.org/tomcat-8.0-doc/config/index.html
在这里插入图片描述

1.Connector 连接器优化

Connector连接器是用来处理客户端请求的,Tomcat主要提供了 BIO,NIO,AIO 三种方式,BIO是同步阻塞,适用于少量请求的情况,NIO是同步非阻塞,适用于连接数多并发高的业务场景。Tomcat8默认使用NIO,一般也不需要去修改,如果需要修改IO模式修改server.xml 的Connector

    <Connector port="8080" protocol="HTTP/1.1"connectionTimeout="20000"redirectPort="8443"maxParameterCount="1000"/>

protocol=“HTTP/1.1” 默认NIO , 直接修改

  • protocol=“org.apache.coyote.http11.Http11NioProtocol” : NIO
  • protocol=“org.apache.coyote.http11.Http11Protocol” : BIO ,Tomcat9已经移除
  • protocol=“org.apache.coyote.http11.Http11AprProtocol” :AIO ,不稳定,性能没有想象中那么高,不用

所以:连接器一般不用优化

2.Executor 线程池优化

配置文档:https://tomcat.apache.org/tomcat-8.0-doc/config/executor.html
Tomcat底层通过Excutor线程池来处理任务,线程池是我们可以优化的点,如果对线程池不理解的同学可以先去恶补一下哦。官方文档中提供了几个重要的参数

maxThreads (int)

该池中活动线程的最大数量,默认为200 ,最大线程数是一个很重要的优化点,我们应该根据机器的性能去合理设置。优化方案:通常情况下通过压测工具去压测Tomcat,然后修改最大线程数,当CPU及内存占用在80%左右,且平均响应时间和吞吐量达到最优,那么这就是一个合理的线程数。

要注意的是线程数不是越大越好,线程数过多,CPU在线程中的上下文切换会越耗时,反而会导致整体性能下降,或者CPU 100%导致系统卡死的情况。但是如果线程数过少会导致无法充分发挥CPU性能,所以通过压测来修改最大线程数是比较合理的方式。网上也有一些通用公式:线程数 = CPU核心数 * (1 + IO耗时/CPU耗时) ,这个也只能作为参考不一定适合所有场景。

minSpareThreads(int)

始终保持活动状态的最小线程数(空闲和活动),默认为25 , 最小线程数可以根据预估并发量来调整,当并发上来minSpareThreads 线程消耗完就会创建新的线程,最大可以创建maxThreads个线程。

maxIdleTime (int)

非核心线程最大空闲时间,超过这个时间线程会被销毁,默认1分钟,该值一般不需要怎么调整。

maxQueueSize (int)

线程池的等待队列,当最大线程数都使用完之后,请求会在队列中排队,默认值为Integer.MAX_VALUE ,当所有的线程都在忙碌,而队列也满了的时候,请求就会被拒绝,该值根据并发量 和 平均响应时间 预估进行设置,
如果不设置很有可能一瞬间来了大量的请求都会被Tomcat接受从而把资源(CPU/内存)消耗空导致服务器宕机。

我们应该对服务器有一个基本的负载能力预估,比如:我只能处理每秒500的请求,那么注定超过500的请求是需要拒绝掉的,我们需要在响应时间和资源利用率之间找到一个平衡点,所以:如果,队列设置太大会导致大量请求等待,造成平均耗时较长。设置太小又回导致大量请求被拒绝,你需要通过监控和性能测试来确定最佳的maxQueueSize值。观察系统的响应时间、吞吐量、资源利用率等指标,并根据这些指标来调整队列大小。这里给点建议:如果是计算型业务,可以设置几十到几百,如果是IO型业务,可以设置到几百到一千。配置如下:

 <Executor name="tomcatThreadPool" namePrefix="catalina-exec-" maxQueueSize="200"maxThreads="200" minSpareThreads="24"/><Connector executor="tomcatThreadPool"port="8080" protocol="HTTP/1.1"connectionTimeout="20000"redirectPort="8443"maxParameterCount="1000" />

3.关闭不要功能

Tomcat有一些组件在某些业务场景下是不需要的,开启后会消耗服务器资源,可以选择关闭

删除AJP

AJP是Tomcat用来兼容阿帕奇的,一般我们都用不到,所以可以在server.xml配置文件中把他删除或者注释

 <Connector protocol="AJP/1.3"address="::1"port="8009"redirectPort="8443"maxParameterCount="1000"/>
关闭热部署

Tomcat的autoDeploy表示自动部署,即热部署。当autoDeploy设置为true时,Tomcat会检查appBase目录下是否有新的或被覆盖的WAR包,并会重新加载web程序。这样,你就可以在不重启Tomcat的前提下部署程序,也是比较消耗性能的,生产环境中可以关闭 : autoDeploy=“false”

      <Host name="localhost"  appBase="webapps"unpackWARs="true" autoDeploy="false">

另外一个就是 reloadable属性,用于控制Tomcat在运行时是否监视Web应用程序的/WEB-INF/classes和/WEB-INF/lib目录下class文件的改动,关闭如下:

<Context docBase="your_webapp_path" path="/your_webapp" reloadable="false">  ...  
</Context>
关闭默认Servlet

Tomcat提供了处理html静态资源的DefaultServlet,和处理JSP的JSPServlet,在齐纳后端分离的项目中一般是不需要Tomcat去处理今天资源。而是通过Nginx做动静分离了,所以是可以把它关闭,找到web.xml把下面配置注释即可

  <servlet><servlet-name>default</servlet-name><servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class><init-param><param-name>debug</param-name><param-value>0</param-value></init-param><init-param><param-name>listings</param-name><param-value>false</param-value></init-param><load-on-startup>1</load-on-startup></servlet><servlet><servlet-name>jsp</servlet-name><servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class><init-param><param-name>fork</param-name><param-value>false</param-value></init-param><init-param><param-name>xpoweredBy</param-name><param-value>false</param-value></init-param><load-on-startup>3</load-on-startup></servlet>

其实还可以关闭的东西有很多,比如Websocket,session 如果你用不到都可以把他关闭,这样就可以把资源节约出来了。

4.启用数据压缩

Tomcat可以通过配置其服务器设置来启用压缩功能,以减少网络传输的数据量并提高网站的性能和加载速度。

<Connector port="8080" protocol="HTTP/1.1"  connectionTimeout="20000"  redirectPort="8443"  compression="on"  compressionMinSize="2048"  compressableMimeType="text/html,text/xml,text/javascript,text/css,text/plain" />
  • compression : 开启压缩
  • compressionMinSize : 小于2M不压缩
  • compressableMimeType :针对于哪些数据进行压缩

二.Tomcat项目压测

1.准备项目

IDEA导入Tomcat源码后,创建一个Servlet,在Service方法中sleep 100毫秒模拟方法的耗时,代码如下

public class MyDefaultServlet extends HttpServlet {@Overrideprotected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {try {Thread.sleep(100);System.out.println("================成功访问到Service================");} catch (InterruptedException e) {throw new RuntimeException(e);}}
}

然后找到web.xml文件对Servet做配置,把url映射到 createOrder 路径

    <servlet><servlet-name>MyDefaultServlet</servlet-name><servlet-class>org.apache.catalina.servlets.MyDefaultServlet</servlet-class></servlet><servlet-mapping><servlet-name>MyDefaultServlet</servlet-name><url-pattern>/createOrder</url-pattern></servlet-mapping>

项目启动后,我们就可以通过 http://localhost:8080/createOrder来访问项目了

2.JConsole指标监控

Tomcat优化不是盲目设置参数,在Tomcat调优的过程中我们需要对相关参数指标进行监控,比如:错误数、线程池、CPU 以及 JVM 内存等等,根据真实的执行情况进行调试。这里我们通过Java自带的监控工具 jconsole 来监控Tomcat 。 使用cmd执行 jconsole ,然后在本地连接里面找到Tomat的启动进程,点击连接
在这里插入图片描述

如果是远程的话需要开启远程连接,我们可以在 Tomcat 的 bin 目录下新建一个名为setenv.sh的文件,内容如下

1 export JAVA_OPTS="${JAVA_OPTS} -Dcom.sun.management.jmxremote"
2 export JAVA_OPTS="${JAVA_OPTS} -Dcom.sun.management.jmxremote.port=8011"
3 export JAVA_OPTS="${JAVA_OPTS} -Djava.rmi.server.hostname=x.x.x.x"
4 export JAVA_OPTS="${JAVA_OPTS} -Dcom.sun.management.jmxremote.ssl=false"
5 export JAVA_OPTS="${JAVA_OPTS} -Dcom.sun.management.jmxremote.authenticate=false"

这样 JMX 的监听端口 8011 就开启了,接下来通过 JConsole 来连接这个端口。
在这里插入图片描述
在概览中可以看到:堆内存,线程数,CPU占用率等几个重要指标,下面是线程情况,我们可以看到工作线程,Poller线程,Acceptor线程情况。
在这里插入图片描述
有了这些指标我们就可以结合压测工具进行压测,一般在极限压力下CPU和内存使用率达到80%较佳,然后不停的调整Tomcat相关参数,比如:线程池数量,JVM内存大小等,以至于达到一个追加的吞吐量。

我们还可以再MBean里面看到一些核心类的值,比如:请求数量,错误数量等等
在这里插入图片描述

3.jemeter工具安装

下载jemeter压测工具:https://jmeter.apache.org/download_jmeter.cgi ,下载后解压,找到bin/jemeter.bat 启动。启动后切换一下语言环境 :options - choose language - chinese
在这里插入图片描述
然后再测试计划中 - 右键 - 新建线程组,线程组就是用来模拟客户端的请求的
在这里插入图片描述
然后设置线程数,和时间,比如:模拟2S钟500个请求,循环发送10次(一共5000个样本),也可以一直循环发送
在这里插入图片描述
添加Http请求,线程组 - 右键 - 添加取样器 - Http请求,然后指定好Tomcat的ip:port和资源路径
在这里插入图片描述
如果需要设置请求参数可以在 参数一栏设置
在这里插入图片描述
然后给线程组添加 结果报告或者 聚合报告来监控压测结果
在这里插入图片描述

4.开始压测

点击:开始按钮,jemeter就会按照设定的线程数向后端发请求进行压力测试了,测试的结果在聚合报告中可以查看,压测效果如下
在这里插入图片描述
大概含义是:总共发送了5000个请求,平均耗时 207毫秒,最小耗时100毫秒,最大耗时685毫秒,吞吐量: 每秒1307个请求。

我们可以把线程组 - 循环勾上,这样可以一直让jemeter发请求进行压测,然后观察JVM情况如下:
在这里插入图片描述
从jconsole中来看,线程峰值是218,堆内存的使用150M都不到,CPU使用率也才1%不到,这是因为我的电脑配置是:12核,24G内存。所以我的资源使用率是很低的,可以尝试把Tomcat线程数调大。

    <Executor name="tomcatThreadPool" namePrefix="catalina-exec-"maxThreads="1000" minSpareThreads="500"/><Connector port="8080" protocol="HTTP/1.1"executor="tomcatThreadPool"connectionTimeout="20000"redirectPort="8443"maxParameterCount="1000"/>

然后把jemeter的压测数据调大,改成1000线程,1s ,然后循环压测效果如下:
在这里插入图片描述
这一次堆内存占用达到了:600M ,线程峰值达到了 1020个线程,cpu使用率:2%左右(CPU太强悍了) ,jemeter压测效果:达到了 8600+ QPS ,如下
在这里插入图片描述
QPS变高了,但是也出现了错误率,错误率一般是因为并发太高,一瞬间请求太多(1000/1s)导致Tomcat处理不过来拒绝了请求导致的。我们继续加大压测力度,jemeter线程数调整为 2000 ,然后再加大 Tomcat线程数到 2000

<Executor name="tomcatThreadPool" namePrefix="catalina-exec-"maxThreads="2000" minSpareThreads="1000"/>

持续一分钟,jemeter 压测效果如下: 尽管我们把线程数调到 2000,压测出来的吞吐量反而降到了6000左右,而且最大耗时接近9S了,所以Tomcat线程数并不是越大越好

在这里插入图片描述
尽管Tomcat官方说的NIO最大可以处理到(1024 * 8)个连接,但是它并不适合你的机器,我们接下里就是不停的去压测和调整线程数获取其他参数来使性能达到最优即可。当然我这里的测试并不精准,因为我使用的是本地PC测试。 项目本身,Jemeter压测工具,Jconsole监控工具都在 本地,都在抢夺CPU和内存,同时还需要考虑 网络开销,所以只能做一个相对值的参考。

在企业中我们应该把应用部署在真实的服务器上,然后在本地通过jemeter远程压测才能测试出真实的效果,然后一边压测一边调试相关参数。

三.其他方面调优

一个系统的性能遵循漏桶原则,所以系统调优需要从多方面进行,比如:应用本身的性能优化的再好,但是业务很耗时,一样会拖慢整体性能。业务很快但是网络不行,也会导致整体性能降低,下面我们从几个方面,讨论一下优化思路。

上面案例中我睡眠了100毫秒来模拟业务耗时,假如我业务耗时只有10毫秒会发生什么情况,是不是性能直接提升十倍呢? 所以我们的业务耗时越小,应用的整体QPS也会越高,在实际的开发中我们优化业务的思路有很多,比如:

1.算法优化

业务越快越好:假如 1个线程1s能处理1次业务,那么200个线程1s能处理200个业务,如果把业务耗时减少到10毫秒,那么整体的性能提升 100倍: 200 * (1000 / 10) = 20000 ,这是理论数据,所以业务越快越好。

优化算法,让代码变得高效,比如:减少嵌套循环,避免大数据循环嵌套处理

2.数据库优化

业务处理难免会和数据库打交道,数据库的数据基于磁盘IO,Java应用和数据库交互也会产生网络IO,当表数据较大时SQL处理就会比较慢,会拖慢整个业务,所以需要做数据库优化,比如:SQL优化,索引优化,主从优化,大表拆解等从而提升查询速度

3.缓存优化

使用缓存:减少数据库操作,减少数据库交互次数,或直接基于Redis 或 本地内存操作。甚至使用Nginx缓存 ,CDN缓存,客户端缓存等

4.优化JVM

高并发下会瞬间创建大量的对象,当内存不够时频繁触发GC,GC的时候会出现STW(停顿应用线程来执行垃圾回收)导致业务处理耗时边长。这个时候就需要进行JVM优化,通过设置JVM参数来减少CG,或者减少Full GC ,选择合适的垃圾回收器也能很大程度提升应用性能。

ps:避免在for循环中new对象哦!!!

5.异步优化

业务能够使用异步就尽可能异步话,可以通过线程池来加大业务处理速度。或者通过MQ消峰,把耗时的部分业务异步处理,能大大提升业务处理速度。

6.硬件优化

给机器或应用增加 内存和CPU,或者网络带宽对性能的提升是非常大的,比如:同样的条件下,2核CPU核8核CPU带来的性能体验是完全不一样的。

7.集群优化

单机优化不动了就可以通过加机器做集群,通过Lvs集群+nginx集群(单机3-5W并发)实现负载均衡,把请求负载到多台Tomcat上一起来处理业务,加入:一台机器能处理2000QPS,那么搞10台就可以处理2W QPS。也可以使用F5这种负载均衡器,他的并发能力可以达到几十万+,可不是Nginx能比的

8.机房分流

如果用户量非常大遍布全国各地,那么可以做CDN分流,比如:成都机房部署一套集群架构能够承担 20W并发,那么根据用户群分布,我可以在上海,北京,杭州等多地都部署几套,然后通过CDN把用户流量负载均衡到对应城市的机房集群中,这样一来每秒百万级请求都可以处理。

所以:说到最后想要抗住高并发,就是加机器,加钱。然而并发量能够每秒上万的项目又有多少,不占20%把。

看到最后的兄弟评论区见:评论区打 “我看到最后啦”

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/9537.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java学习【类与对象】

类和对象 开始我们就不讲那些把大象放冰箱需要几步来引入面向对象的例子了&#xff0c;直接上干货。 在Java中&#xff0c;类是对现实世界中某一类事物的抽象描述。它包含了该类事物的属性和方法。属性用于描述事物的状态&#xff0c;而方法则用于描述事物可以做的事情。对象也…

Redis 支持的 Java 客户端都有哪些?

Redis 是一种高性能的键值存储系统&#xff0c;它以其快速、灵活和可扩展的特性而闻名。在 Java 开发中&#xff0c;与 Redis 交互的方式通常是通过使用 Redis 的 Java 客户端。 这些客户端提供了访问 Redis 数据库的接口&#xff0c;使开发人员能够在 Java 应用程序中轻松地使…

电脑设置在哪里打开?Window与Mac双系统操作指南

随着科技的不断发展&#xff0c;电脑已经成为我们日常生活和工作中不可或缺的一部分。然而&#xff0c;对于许多初学者来说&#xff0c;如何找到并熟悉电脑的设置界面可能是一个挑战。特别是对于那些同时使用Windows和Mac双系统的用户来说&#xff0c;更是需要一篇详尽的指南来…

qml 和 c++类的数据交互

1、 新建一个需要交互的C++类 1)添加QObject头文件 2)添加自QObject的继承 3)添加Q_OBJECT宏 4)使用Q_PROPERTY,定义两个交互的属性,并设置读写的方法和变更属性的信号。 5)添加方法、槽函数和变量 2、在main.cpp中添加实例化对象的QML上下文 1)添加需要QML交互的…

在做题中学习(54):点名

LCR 173. 点名 - 力扣&#xff08;LeetCode&#xff09; 此题有不同的几种解法&#xff1a; 解法一&#xff1a;暴力枚举 O(n); 解法二&#xff1a;哈希表 把原数组丢入哈希表&#xff0c;遍历哈希表&#xff0c;看看哪个数值为0即可。 O(n)空间O(n)时间 解法三&…

Redis如何避免数据丢失?——RDB

目录 1. RDB机制的配置 2. fork()函数和写时复制(Copy On Write&#xff09; 什么是Copy On Write 系统fork中使用Copy On Write机制 3. RDB文件结构 RDB文件内容和内容顺序 InfoAuxFields是rdb信息数据 数据库数据 数据 数据存储格式 字符串编码 操作码 4. RDB的2…

【自研网关系列】数字签名和url动态加密

&#x1f308;Yu-Gateway&#xff1a;&#xff1a;基于 Netty 构建的自研 API 网关&#xff0c;采用 Java 原生实现&#xff0c;整合 Nacos 作为注册配置中心。其设计目标是为微服务架构提供高性能、可扩展的统一入口和基础设施&#xff0c;承载请求路由、安全控制、流量治理等…

【Java EE】数据库连接池详解

文章目录 &#x1f38d;数据库连接池&#x1f338;Hikari&#x1f338;Druid &#x1f340;MySQL开发企业规范⭕总结 &#x1f38d;数据库连接池 在上⾯Mybatis的讲解中,我们使⽤了数据库连接池技术,避免频繁的创建连接,销毁连接 下⾯我们来了解下数据库连接池 数据库连接池负…

「ETL实战」搭建数仓,解决多源业务系统关联分析难题(定制化业务)

在大数据分析盛行的今天&#xff0c;关联分析作为数据挖掘和业务洞察的重要手段&#xff0c;受到了极大关注。然而&#xff0c;随着数据量的激增和源业务系统的复杂性增加&#xff0c;关联分析的性能问题逐渐成为了一个不可忽视的挑战。 本文将介绍借助ETL工具&#xff0c;如何…

大数据面试题 —— 数据库

目录 关系型数据库与非关系型数据库的区别数据库三范式MySQL中 drop、delete、truncate的区别MySQL中 char和 varchar 的区别MySQL中inner join、left join、right join以及full join的区别MySQL中 having 和 where 的区别count(*)、count(1)、count(列名)的区别MySQL中视图和表…

Threejs 学习笔记 | 灯光与阴影

文章目录 Threejs 学习笔记 | 灯光与阴影如何让灯光照射在物体上有阴影LightShadow - 阴影类的基类平行光的shadow计算投影属性 - DirectionalLightShadow类平行光的投射相机 聚光灯的shadow计算投影属性- SpotLightShadow类聚光灯的投射相机 平行光 DirectionalLight聚光灯 Sp…

12.轻量级锁原理及其实战

文章目录 轻量级锁原理及其实战1.轻量级锁的核心原理2.轻量级锁的演示2.1.轻量级锁的演示代码2.2.结果分析 3.轻量级锁的分类3.1.普通自旋锁3.2.自适应自旋锁 4.轻量级锁的膨胀 轻量级锁原理及其实战 引入轻量级锁的主要目的是在多线程环境竞争不激烈的情况下&#xff0c; 通过…

verilog中不重叠序列检测

编写一个序列检测模块&#xff0c;检测输入信号&#xff08;a&#xff09;是否满足011100序列&#xff0c; 要求以每六个输入为一组&#xff0c;不检测重复序列&#xff0c;例如第一位数据不符合&#xff0c;则不考虑后五位。一直到第七位数据即下一组信号的第一位开始检测。当…

C++下使用Matplotlib-cpp的一些配置

Aconda3安装使用 https://mirrors.tuna.tsinghua.edu.cn/help/anaconda/下载 切换清华源 conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/ conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/ con…

修改latex中block中公式与block标题间隔过大的问题

修改block中公式与block间隔过大的问题 如图的block中公式出现了空白:代码见下方 \begin{proof}[证明]\begin{align*}&Z\alpha \beta _XX\beta _YY\varepsilon \rightarrow XZ\alpha X\beta _XX^2\beta _YXY\varepsilon X&\\&E\left( Z \right) \alpha \beta _XE\…

STM32实现1.8寸液晶屏 LCD SPI串口显示屏模块 TFT彩屏(标准库和HAL库实现)

目录 一、所选模块 液晶模块选择&#xff08;淘宝上均有售卖&#xff09; 模块引脚 二、嵌入式单片机型号 三、接线表设计 四、开发环境版本说明 五、标准库实现 六、HAL库实现 七、完整工程&#xff08;内含标准库和HAL库源码&#xff09; 代码链接 一、所选模块 液…

pycharm如何对for循环中第n次循序执行断点

目录 在 PyCharm 中&#xff0c;您可以设置条件断点来实现这个功能&#xff0c;这样只有在满足特定条件时断点才会被触发。以下是设置仅在 for 循环的第 n 次迭代时触发断点的步骤&#xff1a; 设置断点&#xff1a; 首先&#xff0c;找到您想要在 for 循环中设置断点的行。点击…

《应用现代化技术能力成熟度评估模型》介绍

在中国软件行业协会、应用现代化产业联盟以及中国电子技术标准化研究院的指导下&#xff0c;产业多家企业共同支持和参与下&#xff0c;完成的《应用现代化技术能力成熟度评估模型》标准。该标准从应用敏捷、稳定可靠、安全可信、业务智能、成本优化五大维度及22个能力项来评估…

计算机网络学习记录 网络的大概认识 Day1

你好,我是Qiuner. 为记录自己编程学习过程和帮助别人少走弯路而写博客 这是我的 github gitee 如果本篇文章帮到了你 不妨点个赞吧~ 我会很高兴的 &#x1f604; (^ ~ ^) 想看更多 那就点个关注吧 我会尽力带来有趣的内容 计算机网络学习记录Day1 本文基于1.1 计算机网络在信息…

Highcharts 实现3D饼图 tooltip轮播

实现3D饼图&#xff0c;并且轮播显示tooltip 自定义toottip样式 import Highcharts from highcharts; import highcharts from highcharts; import highcharts3d from highcharts/highcharts-3d;highcharts3d(Highcharts); highcharts3d(highcharts); import { useEffect, use…