☰
Gatling压测机资源优化:从CPU到内核参数的全方位调优指南
2026/10/20 9:30:23 网站建设 项目流程

1. 先搞清楚资源模型:为什么压测机自己会成为瓶颈

用Gatling做压测,绝大多数人第一反应是“被测系统能不能扛住”,很少有人先问一句:那台负责发压的机器,自己扛不扛得住?我见过太多失败的压测项目,最终结论不是被测服务性能差,而是压测机先趴了。CPU爆表、DNS解析超时、临时端口耗尽、GC频繁停顿——这些乱七八糟的问题叠加在一起,最后产出的性能报告,测的根本不是目标系统的真实水平,而是压测机自己的“极限运动成绩”。

1.1 这些症状说明压测端已经到极限了

先说一个典型的翻车现场:某测试团队用Gatling对内部系统做并发测试,并发数拉到了8000,结果目标服务器CPU才跑到40%,压测机的CPU却直接顶满,JVM的GC占用超过了总CPU时间的一半。最终的报告里,响应时间曲线乱七八糟,P99从几十毫秒一路飙到几十秒,但目标服务的日志显示,它根本没收到那么多请求。

像这样的场景并不少见。下面这几类症状,只要出现任何一个,都应该先怀疑压测端资源不够:

  • 压测机CPU长时间跑满,目标服务器CPU还有大量余量。
  • 压到一半大量报connection refused或超时,但目标服务日志里实际接收的请求数远小于Gatling侧统计的TPS。
  • 响应时间曲线呈锯齿状,一会儿几十毫秒,一会儿几秒,反复横跳。
  • 换了一台性能更好的电脑跑同一份脚本,测出来的TPS和P99完全对不上。
  • 并发数往上加,TPS却不再跟着涨,甚至出现下跌。

很多人遇到这些情况会怀疑Gatling有问题,或者怀疑脚本写得不对。其实大多数时候,问题就出在压测机自身的资源规划和配置上。

1.2 虚拟用户也是有开销的

Gatling最大的卖点是“事件驱动、低资源占用”,这容易让人产生一个错觉——虚拟用户不占资源。实际上完全不是这么回事。Gatling底层基于Akka的Actor模型,每个模拟用户对应一个Actor,这个Actor在JVM里有自己的状态、消息队列和定时器,再加上Netty的IO线程池,CPU和内存的开销都是实实在在的。

我习惯用这个类比来理解:Gatling的虚拟用户像餐厅里的点餐平板,不是每个客人配一个厨师(线程),而是一个厨师(事件循环)同时盯着几十个平板。但平板本身是要耗电的,屏幕要亮、网络要传数据、内存要存状态。当并发数从1000提升到5000,意味着JVM里Actor数量翻了5倍,GC压力也是非线性上涨,处理不当整个压测机都会被拖垮。

从资源构成来看,Gatling压测端的开销主要在四个维度:

  • CPU:请求构造与响应解析、DNS解析、日志序列化、JSON处理。
  • 内存:每个用户的状态对象、请求缓存、响应体存储。
  • 网络:TCP握手成本、连接池占用、TIME_WAIT状态堆积。
  • 磁盘I/O:结果日志(simulation.log)的持续写入。

这四个维度任何一个卡住,都会直接体现在压测结果上。所以做Gatling资源优化,本质上就是把这四个维度的瓶颈逐个拆掉,让压测机能稳定地、持续地“输出压力”。

1.3 如何确认瓶颈到底在压测机还是在目标机

我在实际项目里会用一套很简单的“资源余量校验法”,分享给大家。

第一,边压边看压测机自身的资源曲线。CPU使用率、JVM堆内存、GC停顿时间、网络连接数,全部记录下来。压测机的CPU如果长期超过70%,数据的可信度就要打个问号。

第二,同时看目标服务器的负载。CPU、内存、带宽、连接数,和压测机侧数据对比。

第三,做一次降并发对照实验。把并发量减半,跑同样的场景,观察目标服务器的TPS变化。如果目标服务器TPS也跟着减半,说明发压端已经接近饱和,当前测试结果不可信,需要提升压测机能力;如果TPS几乎不变,说明瓶颈确实在目标服务器上。

这套方法我几乎每次压测前都会做一轮,省下了大量排查时间。

2. 硬件配置实操:CPU、内存、磁盘怎么调才不拖后腿

硬件是压测机的底子。很多团队直接用开发用的笔记本跑压测,这没问题,但要清楚笔记本在长时间高负载下的行为——功耗限制、温度降频,都会让压测数据变得不稳定。有条件的话,压测机尽量用一台配置明确的物理机或云主机,并且专门为压测预留,不跑其他业务。

2.1 CPU选型与并发能力的关系

先解决一个基础问题:到底需要多少核才够?

这没有固定答案,因为不同场景对CPU的压力差别很大。一个简单的GET请求,响应体只有几百字节,单核跑出几千RPS并不难;但如果每秒钟要构造复杂JSON请求体、解析大响应、做签名计算,CPU开销可能翻好几倍。

我一般用经验法则来估算:在压测机上观察CPU利用率,保持压测机CPU在50%以下,就算有充足发压余量;一旦超过80%,数据可信度就要打折扣。很多人在压测时有个误区,以为目标服务器没跑满就是服务端性能好,实际上可能是压测机已经发不出更多压力了。

选型上还有两个注意事项:

  • 压测机优先选物理核数明确、频率稳定的CPU。超线程在并发场景下性能提升有限,甚至在CPU密集的场景下因为资源争抢导致抖动,如果机器支持,建议在BIOS层关掉超线程再跑压测。
  • 避免用笔记本长时间压测。不是说不能用,而是笔记本散热和功耗限制会导致CPU降频,表现为压测数据在某个时间点之后突然变差,影响结果判断。

2.2 JVM堆内存与GC调优实操

Gatling跑在JVM上,内存管理问题绕不开。最典型的坑就是默认堆内存不够用。

Gatling默认启动脚本里给JVM堆内存留的额度并不大,我记得默认是1G左右。压两三百并发没问题,但压到几千并发,堆内存很快就满,频繁Full GC会让吞吐量“一卡一卡”的,RT曲线直接变成锯齿状。

调整方法很简单,新版Gatling可以通过环境变量GATLING_HEAP来设置堆内存,旧版本则是改启动脚本里的JAVA_OPTS。例如:

export GATLING_HEAP="8192M"

堆内存给多少合适?我一般按用户规模和响应体大小来估算。简单接口、响应体几百字节,1000个虚拟用户大概2到3G就够用;当并发到5000以上,就需要8G以上了。但要注意,堆内存不是越大越好。给了16G堆,如果实际只用了2G,GC反而会因为堆太大而产生更长的停顿周期。关键指标是让堆内存保留30%左右余量,同时观察GC日志。

GC调优方面,HOTSPOT默认的GC算法在Gatling这种高并发、短请求对象的场景并不合适,建议显式启用G1GC,并设置停顿时间目标:

export JAVA_OPTS="-XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:MaxMetaspaceSize=512m"

这里MaxGCPauseMillis设置到200毫秒是实践里比较平衡的值,既不让GC过于频繁,也不会出现大停顿。如果是超大堆(16G以上),可以把目标调到100到150毫秒试试。每次调完GC参数,务必跑同一组负载,观察GC日志里的停顿频率和时间,确认压测机GC占用CPU的比例降下来。

另一个内存杀手是响应体。如果被测接口返回几MB的JSON,而脚本里又把响应的全部内容保留到验证阶段,内存立刻会被打爆。对纯压测场景,建议关闭详细响应体断言,或者只保留状态码和关键字段校验。

2.3 磁盘I/O与文件句柄:容易忽略的隐藏瓶颈

磁盘这块往往是最后一个被发现的瓶颈。Gatling默认会把所有请求的详细信息写入simulation.log,包括每个请求的URL、状态、耗时等。并发量大了之后,日志写入速度跟不上请求产生速度,压测机就开始整体变慢。

解决办法很直接:压测时把日志级别调高,避免每个请求都刷盘。Gatling的logback配置文件里,把logger的级别从INFO改为WARN或ERROR,日志写入量会大幅度下降。同时建议把压测生成的results目录放到SSD或独立磁盘上,避免跟系统盘抢I/O。

文件句柄限制是另一个高频坑。默认的ulimit -n在桌面系统上通常是1024或4096,高并发压测时打开的socket和文件句柄很容易突破这个值,然后就会开始报Too many open files。压测前必须把上限调大:

ulimit -n 65535

同时检查系统级的fs.file-max是否够大,不够就在/etc/sysctl.conf里调整。这步不复杂,但漏掉了会让整个压测在某个并发点上戛然而止。

3. 网络配置最佳实践:让连接通道畅通无阻

硬件资源调好之后,网络层面的问题就开始浮出水面。Gatling作为压测工具,每一个虚拟用户的请求都要走真实的TCP连接。网络配置不合理,压测机上万个连接状态堆积,性能就崩了。

3.1 本机回环测试与远程压测的选型判断

压测对象是本地服务时,有人图方便直接用http://localhost:8080来压。这种做法不是不行,但要清楚它测的是什么:回环地址走的是loopback接口,不经过物理网卡,网络协议栈开销极小,测试结果反映的是“应用逻辑+本机内核协议栈”的性能上限,会更偏理想化。

真实业务场景建议用远程压测,也就是压测机通过物理网卡访问目标服务器的真实IP。这样能测出TCP握手成本、链路带宽、网卡吞吐等真实损耗。尤其到公网环境,带宽和延迟会成为夹在中间的约束条件。

有个场景我特别提醒大家注意:本地回环压得很顺,性能指标很漂亮,换到远程环境数据直接掉一半以上。这时候不要先怀疑服务端代码,先算链路账:并发数乘以每个请求的平均大小,得到的就是理论吞吐带宽需求。比如1000个并发,平均每个请求加响应是50KB,那就是50MB/s,对应需要400Mbps以上的带宽,普通百兆网络根本扛不住。

还有一个小技巧:远程压测前,把目标域名直接写进压测机的/etc/hosts,或者压测脚本直接用IP地址。这样可以避开公网DNS解析的随机性,让压测数据更稳定。

3.2 Linux内核参数调优:这些配置让压测机跑得更稳

压测机作为高频TCP连接发起方,Linux内核的网络参数默认值多半是偏保守的,需要针对压测场景调优。以下是我每次都会检查的几项。

首先是临时端口范围。Linux为客户端连接分配的临时端口(ephemeral port)默认范围通常是32768到60999,大约28000个可用端口。高并发下很容易被耗尽,新连接就没端口可用了,表现出来就是大量Connection refused。把这个范围扩大:

net.ipv4.ip_local_port_range = 1024 65535

其次是TIME_WAIT状态的优化。TCP四次挥手后,主动关闭方会进入TIME_WAIT状态,默认等待60秒。压测场景里大量短连接导致TIME_WAIT堆积,可用端口被占住不放。对于压测机这种主动发起大量连接的场景,可以开启连接复用,同时缩短TIME_WAIT的等待时长:

net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_fin_timeout = 30

注意tcp_tw_reuse主要对客户端场景有效,压测机正好是典型的客户端角色,所以这个配置是合适的。这里有个容易被误导的点:很多文章建议同时开启tcp_tw_recycle,但新版内核已经移除了这个参数,而且它在NAT环境下会导致连接异常,不要在压测机上用。

然后是连接队列参数。高并发下大量TCP SYN包同时到达,如果backlog队列太小,握手成功率会下降。把系统级的accept队列调大:

net.core.somaxconn = 1024 net.ipv4.tcp_max_syn_backlog = 4096

最后是socket缓冲区。对大多数短请求压测场景,默认缓冲够了,但如果被测接口有大量大响应体(比如几百KB到几MB),适当调大系统级socket缓冲值会有明显帮助:

net.core.rmem_max = 16777216 net.core.wmem_max = 16777216

调完之后要立即生效可以用sysctl -p,但更建议写入/etc/sysctl.conf,避免重启后丢失。

需要强调的是,这些内核参数是为了“压测机能够制造更大压力”而优化的,它让压测机能模拟更极限的并发场景,但不代表真实用户的网络环境。所以调优后测出的数据,适合用来求性能上限,不适合当作实际用户体验的直接参考。

3.3 请求发送与连接管理的高频陷阱

Gatling在HTTP层有几个配置项,对压测机的资源消耗影响非常大,处理不当会产生大量无效开销。

第一是连接池。Gatling为每个虚拟用户维护HTTP连接,默认情况下连接池大小有限(我记得是6左右)。当并发很高时,大量用户同时创建连接,握手成本会吃掉不少CPU。实际操作中,可以根据被测系统的承载能力适当调大连接池上限,或者改为共享连接模式,减少重复握手的次数。这个参数在gatling.conf里可以调整,具体以你的Gatling版本的配置文件为准。

第二是关闭HTML资源下载。如果压测目标是纯接口,而脚本里没有显式关闭资源下载,Gatling会像浏览器一样去请求页面里的css、js、图片,请求量成倍增长不说,还会干扰接口本身的性能数据。纯API压测务必确保不开启fetchHtmlResources。

第三是DNS解析。这是个比较隐蔽的瓶颈。Gatling基于Netty做异步IO,系统默认的DNS解析线程和缓存策略在并发大时容易打满。表现为压测开始后CPU飙升但TPS上不去,加并发也不见效果。最直接的规避方案是压测前把被测域名写入hosts文件。

4. 不同规模压测场景的资源配置模板参考

前面讲了一堆理论和参数,这一节给三套可以直接抄作业的配置模板。按场景对号入座,省得每次都在配置文件里反复试错。

4.1 开发联调与小并发场景(并发50到200)

这个场景通常发生在功能联调阶段,主要目标是验证接口正确性和基本性能,不追求极限并发。

这类场景对资源要求很低,压测机用开发机就可以。JVM堆内存给到2G到4G,启动时显式开启G1GC,其余参数不需要额外调优。内核参数保持默认即可,因为并发量不高,不会出现端口耗尽或TIME_WAIT堆积的问题。日志可以全开,方便排查业务问题,因为请求量不大,日志写入不会拖累性能。

4.2 中规模压测场景(并发500到2000)

这个量级开始对压测机有硬性要求了。单台压测机需要有4到8个稳定物理核,内存16G起步,JVM堆内存建议给到8G,留出足够的GC余量。

网络层面,需要完整执行上面说的内核参数调优,特别是临时端口范围、连接复用、队列长度三项。日志必须调高级别,避免每一个请求都写入磁盘,否则压测后半段性能会明显下滑。

另外建议在正式压测前把被测域名加入hosts,排除DNS干扰。如果目标服务在公网,还要先确认带宽足够,算好“并发数乘请求大小”的理论带宽需求。

4.3 高并发与多机分布式压测(并发5000以上)

单机跑到5000以上并发,硬件和内核参数都已经调到很极限了,这时候最合理的方案是上分布式压测,用多台压测机分摊压力。

多机压测有几个细节值得留意:

  • 各压测机的系统时间要同步(NTP),否则不同机器上报的延迟数据会出现系统性偏差,合在一起统计时P99可能是错的。
  • 压测机和目标服务尽量在同一内网网段,避免公网抖动带来的噪声。
  • 每个压测机的并发分配建议均匀分布,但如果不同压测机硬件性能差异大,弱机器会被优先打满,整体数据被拉低。这种情况下更应该按机器能力分配用户数,而不是简单平均分。
  • 汇总数据时,各机的simulation.log要打上机器标识,便于按机器维度拆分检查。

分布式压测的瓶颈往往不在于Gatling本身,而在于网络链路和压测机之间的协调。建议在正式测试前先做一次全链路的预压,确认每台压测机都能稳定输出,再正式跑大批量。

5. 常见问题与排查技巧实录

这一节把我在实际项目里踩过、帮别人排查过的坑集中整理一下,按症状给排查思路,碰到类似问题时可以直接对照着查。

5.1 压测机CPU 100%,目标服务器CPU很低

这是最典型的压测端瓶颈。排查顺序:

  1. 先看JVM GC开销。如果GC线程占了大量CPU,优先加堆内存、调GC参数。
  2. 再看DNS。用hosts文件或IP直连,排除DNS解析阻塞。
  3. 检查是否开启了日志全量写入,日志序列化在并发高时非常吃CPU。
  4. 检查脚本里是否有大量正则表达式或重复的响应体处理逻辑。

以上都排除后,如果压测机CPU依然跑满,说明这台机器的发压能力已经到顶,需要降低并发或者上分布式。

5.2 连接池溢出或大量Connection refused

高频连接场景下,先看系统当前连接状态:

ss -ant | grep TIME_WAIT | wc -l ss -ant | grep ESTABLISHED | wc -l

如果TIME_WAIT数量异常多,说明连接释放不及时,按前面提到的内核参数调优处理:扩大临时端口范围、开启tw_reuse、缩短fin_timeout。如果依然不够,把压测机网卡的多队列、中断绑定优化一下,或者直接换更强的机器。

5.3 RT曲线锯齿状:一秒好一秒差

锯齿状曲线是排查重点。如果压测机CPU和GC都正常,那大概率问题出在目标服务端的负载均衡、缓存淘汰或线程池排队上,不要一直在压测端找原因。

简单的确认方式:把并发减半,如果锯齿现象消失,说明是压测机在临界点挣扎;如果锯齿还在,果断转到目标服务端排查。还有一种情况是锯齿同步出现,固定每隔几秒一个波谷,优先检查压测机上是否有定时任务或监控代理在周期性抢CPU。

5.4 换了压测机数据对不上

这个问题我见过太多人踩了。两台配置不同的机器跑同一脚本,数据差异并不一定说明目标系统不稳定,而是发压能力的差异导致的。

性能弱的压测机会“温和地”掩盖目标服务的真实瓶颈:它发不了那么多压力,目标服务器自然“表现优秀”。性能强的压测机一上来就把目标服务压到了真实极限,数据自然“变差了”。所以在跨机器做性能对比时,必须先确认压测机自身的资源消耗情况,只有当压测机资源充足且留有大量余量时,数据才具备可比性。

5.5 一个值钱的排障习惯:记录压测端自身的监控数据

我最后想分享一个习惯,它帮我在无数个压测项目中快速定位问题:每次压测,不只要记录目标服务器的TPS和RT,还要同时记录压测机自己的CPU、内存、网络、JVM GC曲线。这些数据平时看起来没什么用,但一旦数据异常,它们就是定位瓶颈的第一手证据。

我个人现在执行的流程是:压测正式开始前,先做一次“冒烟校准”。用10%的计划并发跑3分钟,重点不是看目标服务的数据,而是确认压测机CPU在50%以下、GC没有异常、端口和连接数没有单边上涨。三个条件都满足,再往上加压。这套流程帮我在多台机器上省下了大量无效测试时间,建议你也试试。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询