一次tomcat压测调优记录

1. 前言

该tomcat web应用承担集团登录注册页面功能,对性能有一定要求,由于先前没有太多相关经验(只压测过一个dubbo服务),这次调得比较艰辛,便做个记录。

2. 调优过程

起初没有给运维任何tomcat配置要求,同时也没留意去确认tomcat配置,这个导致了后续压测过程各种诡异的问题。

a.在压测初期,持续请求10分钟左右出现无请求进来,netstat查看的tomcat所在服务器存在大量CLOSE_WAIT的连接。 
CLOSE_WAIT的连接一般是自己程序中缺少关闭连接等引起,但是查看程序也没发现哪里没有关闭,而且大多CLOSE_WAIT是与浏览器端的http协议下的tcp连接。后经运维排查是centos自身的BUG引起,升级到centos-release-6-6.el6.centos.12.2.x86_64后解决。

其中对于CLOSE_WAIT和TIME_WAIT的TCP连接起初一直不太理解是怎么出现,怎么解决,后详细查看TCP四次挥手断开连接了解了整个过程。 (图片来自网络)
 
比如客户端应用程序发起CLOSE信息,服务端接收到后进入CLOSE_WAIT状态并做ACK,之后服务端程序发起CLOSE信息,客户端接收到之后进入TIME_WAIT,服务端收到客户端的ACK之后进入CLOSED状态,客户端TIME_WAIT需要等待到超时才进入CLOSED状态。

基于此,服务器端出现大量CLOSE_WAIT不是一个正常的状态,首先需要确认CLOSE_WAIT状态对方的IP,再查看这个IP对应的代码是否缺少关闭连接。 
但是如果出现大量TIME_WAIT,不是太要紧,只要不占满句柄就行,如果真的占满了可以尝试修改内核TCP超时时间和TCP的TIME_WAIT重用。

b.然后压测500个并发出现connection timeout和read timeout,这种情况基本是在请求数超过了配置的最大值,一开始找运维排除nginx和vm的限流,然后再查看tomcat的限制,发现tomcat未配置最大线程数,默认情况最大线程数是200,最大等待队列100,然后修改tomcat的server.xml配置

<Connector port="8080" protocol="org.apache.coyote.http11.Http11NioProtocol" connectionTimeout="30000"
        enableLookups="false" maxThreads="2048" minSpareThreads="100" acceptCount="512" URIEncoding="UTF-8"/>12

调整protocol使用nio的,调整最大线程(maxThreads)为2048用于压测,最小空闲线程数(minSpareThreads)100,接收请求队列最大(acceptCount)为512 
,到这里压1000或者2000并发都不会出现拒绝请求的情况。

这里再细化下connection timeout和read timeout,connection timeout处于连接还没建立就超时的状态,如果不是网络问题,多半是maxThreads+acceptCount不够处理并发请求,可以尝试增加这两个值;read timeout是建立连接后,一种是等待在队列太久没处理导致超时、一种是程序自身执行时间太久,可通过access log记录请求执行时长排查。

c.再压一段时间后,出现请求响应慢,请求进不来,此时大多是connection refused(由于先前调整的线程池,一直还在排查线程池问题),后来查看gc日志发现一直在做full gc并且老年代内存无法释放,由于没有指定过gc方式,当时以为是gc引起,所以先调整了gc配置为cms 
记录gc日志和服务挂掉时dump内存配置

-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/vol/logs/heap.bin -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:/vol/logs/gc.log1

修改为cms的gc方式的配置

-XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=10 -XX:+CMSClassUnloadingEnabled -XX:+CMSParallelRemarkEnabled -XX:MaxTenuringThreshold=15 -XX:CMSInitiatingOccupancyFraction=70 1

对CMS的GC理解不深,大致知道默认的gc方式是阻塞应用,CMS采用并发标记清理的方式,后续再翻书学习下。TODO

d.通过修改gc方式得到了一定的缓解,在压测不到30分钟左右时发现又出现吞吐量降低,响应非常慢的问题; 
大致知道是有很多对象没释放,通过jmap查看前20个占用最大的对象(./jmap -histo pid | head -n 20 ),好像都还好,最大的是session不过也才300多M(估计是当时刚好不在压力期间已经被释放完了); 
然后在出现慢的时候dump了全部内存下来,但是实在太大了(5G),下载太久; 
之后把堆内存调整为1G并压到出现慢,查看dump也是session最大,占百分之八十左右,这时候还没意识到是session的问题,以为1G太小看不出根本问题; 
再把堆内存调整为2G并压到出现慢,查看dump发现session占了1.5G,突然灵光一闪,我这边request session是封装放到redis的,本地的tomcat session占这么大是不对的。。。(闪得也太迟了点吧),然后把tomcat的session超时时间设置成一分钟果然不会再出现频繁FULL GC的问题。再查看代码,原来是封装的session的地方脑抽的写了个super.getSession(true)。默认配置下每次请求来都会生成新的session,并且超时时间是30分钟,在内存中占30分钟才被销毁!!

看起来上面的问题都非常明显。。。但是在一个压测环境下去找这些问题源头是什么真是麻烦死了。。

e.好了,最后一步,光明就在不远的前方。压测结果不算太理想,tps在4800左右,不过最起码长时间压下来算是稳定了。之后通过jprofile工具查看cpu的消耗在哪来分析单次请求耗时点。 
 
jprofile去连接远程机器时需要在远程机器开启agent,执行bin/jpenable后选择是给gui访问然后填写端口即可。

f.这里再记录下稳定压测后想做一些优化遇到的问题: 
这边在尝试提高TPS的时候,发现查看所有的压测指标资源貌似都未被占满,但是吞吐量始终上不去(应该是哪个未留意到的资源被占满了),tomcat线程池一半不到,cpu百分之70左右浮动,堆内存1G不到,磁盘IO在百分之八左右,带宽占用很低(速度在80k/s)tcp连接数在1W+ 
排查这个问题时首先怀疑到可能是使用到的一个后端http服务跟不上,通过mock掉该服务后无明显提升 
后怀疑是tomcat没配置正确,然后空压一个test.jsp,tps在2W+ 
再怀疑是redis没使用正确,在空test.jsp前使用一个redis session filter,相当于只压redis,tps也在1W8到2W+ 
这个时候已排除了所有的外围服务,确认了是自身程序的问题,通过jprofile去查看耗时较大的一个函数,发现ua解析较慢,通过将解析结果放到threadlocal,tps得到小幅增长,从4800到5000+ 
然后发现log4j的block较多,通过将log4j的级别调整到ERROR后,tps一下能到7000+,但是还是不能理解,后有同事说可能是磁盘IO次数的限制,这个当时没有关注到的点,但是环境已被撤除。

3. tips

3.1 压测开始前准备 
设定压测业务场景:比如用户从加载登录页面到使用账号密码登录完成 
准备压测环境:与线上同等配置的服务器以及数据量,资源无共享的纯净环境 
根据线上需求制定压测目标:比如TPS>1000,平均响应时间<100ms,错误率<0.01%,稳定运行,CPU<80%,无内存溢出,Full GC间隔时长>30分钟,gc时长<150ms 
配置资源数:各线程池、连接池大小,内存分配大小,tomcat连接数

3.2 制定压测目标 
TPS >= (80% * 单日访问量) / (40% * 24 * 60 * 60),线上80%的请求量几种在40%的时间,根据线上情况进行调整

3.3 如何配置资源数 
根据平均响应时间和TPS计算tomcat线程池大小,TPS/(1/RT),比如TPS>=5000,平均响应时间<=100ms,线程池大小配置5000/10,再往上调整CPU数目的整数比例(这种计算方式不一定好,主要响应时间这个不太好定,上下会相差较大,) 
数据库连接池或其他线程池大小可先设定为tomcat线程大小的一半,根据压测结果再进行调优

3.4 压测指标及对应查看工具 
CPU,top/visualvm 
内存,free查看系统内存使用情况/jmap可查看最大使用的内存对象以及dump全部堆/visualvm/mat查看dump分析内存泄露对象及其引用 
磁盘IO,iostat 
磁盘空间,df -hl 
网络带宽,iptraf 
连接数,netstat 
线程,top -Hp pid/jstack pid/visualvm/tda查看线程dump/jprofile监控实时线程block情况 
FULL GC,-Xloggc:/path/gc.log开启gclog后查看gclog、jstat -gc 
响应时长,jprofile查看耗时较大的函数

3.5 定位瓶颈分三步走 
检查压测端瓶颈,可通过压一个恒定标准测试(比如压空的test.jsp),基本就在压测初期检查一次就差不多 
通过查看压测指标变化情况定位资源瓶颈,再根据资源瓶颈定位程序瓶颈 
比如根据监控显示每次GC后堆内存呈上涨趋势,可在两次GC之后分别查看占用内存较大的对象,对比这些对象的数目和大小,如果有明显上涨较厉害的,可特别查看下 
TODO图片 
在第二步查看问题中,可能出现影响因素较多的情况,这样可通过mock掉部分怀疑的因素或者单压某怀疑因素 
比如系统中使用的外围服务包括数据库、redis,现在怀疑可能是查数据库或者在查redis的时候导致吞吐量上不去,此时可通过mock掉数据库的查询查看下吞吐量是否有提升,如果有提升则说明多半是数据库查询导致

3.6 优化 
在定位到瓶颈点之后,相对来说优化就没那么复杂,无外乎对瓶颈资源的少用、复用和交换资源。 
比如在程序中一个请求下来会查询N次账号信息,可以考虑把该账号信息放置到缓存或者内存之类的。 
可以从业务角度去考虑优化,比如某个扩展业务直接影响到主业务的性能,且该扩展业务本身不是那么重要,可以考虑通过缩减该扩展业务的方式做到优化。

时间: 2025-01-06 22:52:10

一次tomcat压测调优记录的相关文章

基于Dubbo的压测调优实例

不久前参与开发了一个基于dubbo分布式框架的底层账单系统,并实现了其中的一部分业务接口,目前需对这些接口进行压测,以评估生产环境所能承受的最大吞吐量.笔者以其中一个查询接口为例来回顾此次压测的整体流程. 压测准备: 1.调用查询接口的测试jar包,作为dubbo-consumer,依赖了查询服务的api,测试module基于maven开发,执行maven clean package即可通过编译得到jar包 2.JMeter:Apache组织开发的基于Java的压力测试工具 方案: 无限次请求查

Oracle SQL调优记录

目录 一.前言 二.注意点 三.Oracle执行计划 四.调优记录 @ 一.前言 本博客只记录工作中的一次oracle sql调优记录,因为数据量过多导致的查询缓慢,一方面是因为业务太过繁杂,关联了太多表.面对复杂的业务场景,确实有些情况是需要关联很多表的.当然有些情况是可以将业务实现放在Java代码里,有些情况可以不要关联很多表. 二.注意点 对于SQL调优,不要马上就说加索引什么的,加索引不一定就能解决问题的,加错索引,反而会导致查询变慢,注意加索引的同时也会影响数据库写数据的速度. 三.O

tomcat安装部署调优

工作中遇到了在linux系统中部署tomcat,由于是在生产环境中部署,还是些许谨慎,这里把部署的方案截图展示. 1.解包 tar -xvf apache-tomcat-7.0.68 2.修改端口号 在解压出来的目录结构中有conf目录,需要修改的是server.xml目录 这里要注意,端口号不能大于65535,因为都是使用的tcp连接,tcp的端口号不能超过65535,在一台主机上部署的多个tomcat的端口号也不能相同,因为只要一个端口在监听状态,另一个进程无法将该端口号激活. <Serve

Kettle数据同步速度调优记录

Msyql到Vertica 1.mysql中在openshop 数据库中选择其中一个300W左右数据的表 create table ip_records_tmp_01 AS SELECT * FROM ip_records_tmp t WHERE t.datetime<= '2015-05-01' 2.vertica创建表ip_records_tmp_01,注意字段类型和mysql有点不一样. 全量抽取2478130条数据,耗时30s,速度不错! 3.在mysql中新增972948条数据,删除4

Mysql Tomcat C3p0 系统性能调优个人总结

系统信息 应用逻辑 就是用c3p0 到数据库查询数据并http返回Json数据 1 调优前的最初的测试结果   JMeter test result No. Type Original 1000 data bigger 1 500Connection 250 query/S 63q/S 70q/S 2 1000 connections 255q/S 57q/S 65 q/S 这个数据是从程序的log 中打印出的 数据库select语句 中得出的结果(正确与否后面会有讨论). 2 经过IOD系统打

有关tomcat的性能调优【待完善】

tomcat的性能优化-------------------Tomcat的默认配置作为生产环境,尤其是内存和线程的配置,默认都很低,容易成为性能瓶颈.有关优化可分为:内存.线程.IO. 一.内存优化[调优内存是最直接的方式]linux下的catalina.sh中 添加:JAVA_OPTS='-Xms512m -Xmx1024m -XX: PermSize=256M -XX:MaxNewSize=256m -XX:MaxPermSize=256m' 重新启动服务 二.线程优化<Connector

Mac压测工具(Siege)记录.md

微信活动推广,时常瞬时并发特别高,比如一个百万粉丝的公众号,后台推送一个H5的活动,我们公司的应用在短时间内基本处于不可用的状态,如何保证高并发下的高可用呢? Mac下安装Siege,作为开发人员可进行简单的模拟高并发,确保服务的可用性,目前容器有Tomcat和IIS,需要分别优化. 一.修改Mac文件描述符限制 在压测开始前,需要确保open files足够大,否则会报Too many open files错误,可以通过ulimit -a查看,默认是256. 使用ulimit -n 10000

Tomcat的JVM调优实战

一些调优点在上篇日志中已写到,在此不做说明 直接使用Jmeter进行调优测试吞吐量Code package cn; import java.io.IOException; import java.util.Map; import java.util.WeakHashMap; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.Htt

tomcat jvm 内存调优 适用于 JDK 6 &amp; 7

参考:https://blog.csdn.net/m0_37327416/article/details/76185051 1.jvm内存管理机制: 1)堆(Heap)和非堆(Non-heap)内存 按照官方的说法:"Java 虚拟机具有一个堆,堆是运行时数据区域,所有类实例和数组的内存均从此处分配.堆是在 Java 虚拟机启动时创建的.""在JVM中堆之外的内存称为非堆内存(Non-heap memory)". 可以看出JVM主要管理两种类型的内存:堆和非堆.简单