1. 10W QPS 压测到底难在哪先算一笔账再动手聊 JMeter 分布式压测这件事绕不开一个现实单机 JMeter 跑到三五千 QPS 就开始喘想摸到 10W QPS 这个量级光靠多开几台机器是压不上去的。我见过太多团队把 20 台 4C8G 的机器拼成一个集群结果压测跑起来总 QPS 卡在 6 万上下CPU 全部 30% 出头谁都不忙但数字就是上不去。这种机器闲着、指标不到的局面八成是架构瓶颈而不是算力不足。这一篇主要写给几类人正在做性能验收、需要给出 10W QPS 级别压测结论的测试或运维同学需要评估系统容量上限的后端开发以及第一次接触 JMeter 分布式模式、被remote_hosts和 RMI 折腾过的同行。内容会从线程数怎么算、负载机怎么调、脚本怎么瘦身、集群怎么组、结果怎么读一路讲到踩过的坑。读完你应该能自己算出一套合理的机器配置也能判断压测数字上不去时问题出在哪一层。先说结论10W QPS 这个量级JMeter 是能打的但它不是打开就能用的工具。它本身是一个 Java 应用每发起一次请求都要走一遍线程调度、取样器、监听器、结果收集的链路任何一个环节留着多余开销乘以 10 万就是灾难。所以整件事的核心逻辑就一句话把 JMeter 当做一个需要被调优的被测系统来对待。1.1 一条公式定并发数与机器数压测最常被问的问题是我要开多少线程。这个数字不该拍脑袋它由目标 QPS 和被测接口的响应时间共同决定。最朴素的推导是利特尔法则Littles Law并发数 目标 QPS × 平均响应时间(秒)假设目标 100000 QPS接口平均 RT 是 20ms那么理论并发数是 100000 × 0.02 2000。注意这是理论最小并发实际必须留余量因为 RT 不是恒定的GC、网络抖动、被测服务排队都会让 RT 拉长一旦 RT 变成 40ms同样的线程数就只能打出 5 万 QPS。生产实践中我一般乘一个 1.3 到 1.5 的系数实际并发数 100000 × 0.02 × 1.4 ≈ 2800接下来的问题是这一台机器能不能扛住 2800 线程。JMeter 用的是线程模型每个虚拟用户对应一个 JVM 线程线程本身要占栈空间默认 1MB 的-Xss可以调到 256KB还要参与上下文切换。经验值是这样的负载机规格单机可稳定承载线程数对应可打出 QPSRT 20ms2C4G200 ~ 3001 万 ~ 1.5 万4C8G500 ~ 8002.5 万 ~ 4 万8C16G1000 ~ 15005 万 ~ 7.5 万16C32G2000 ~ 300010 万左右极难稳定这张表是基于 HTTP 短连接、响应体 1KB 以内、无复杂断言的前提估的只能当参考。真实选型最好先做单机基准测试一台机器固定 500 线程跑 5 分钟看 CPU、GC、网络三项找出QPS 不再随线程线性增长的那个拐点那个拐点就是这台机器的真实上限。我一般的做法是把单机目标定在能力上限的 60% 到 70%剩下的余量留给波动。按这个思路10W QPS 配 20 台 8C16G 的负载机每台 1000 线程、承担 5000 QPS是比较稳妥的配置。如果想省机器12 台也能跑但单机负载逼近上限任何一个节点掉链子都会让整体数据失真。压测集群不是越省越好稳定打满比勉强打满重要得多。1.2 单机天花板到底卡在哪里很多人调优 JMeter 只会加-Xmx加完发现没用。因为单机 QPS 上不去瓶颈通常不在堆内存而在下面这几个地方第一是CPU 上下文切换。线程数越多切换越频繁vmstat里的cscontext switch列会飙到几十万。切换本身是纯开销不产生任何 QPS。所以线程数不是越多越好过了拐点之后加线程反而降 QPS。第二是结果收集与写盘。JMeter 每完成一次取样都要生成一条 SampleResult 记录。如果监听器没关或者结果写的是 XML 格式这部分开销能占到总 CPU 的 20% 以上。分布式模式下更麻烦slave 要把结果通过 RMI 回传给 mastermaster 单点接收几十万条记录往往 master CPU 先满而 slave 全在摸鱼。第三是网络与端口。10W QPS 意味着每秒 10 万个 TCP 连接建立短连接场景客户端的本地端口、TIME_WAIT 状态、conntrack 表都会成为硬约束。这部分在第二章细说。第四是JVM GC。JMeter 在高压下会产生大量短命对象如果堆设得过大又用了并行 GC一次 Full GC 停几百毫秒采样曲线就会出现明显的锯齿。G1 是当下比较稳的选择堆 8G 到 16G 足够再大反而增加 GC 停顿风险。1.3 分布式组网的三种形态与取舍JMeter 官方支持的分布式模式是 master-slave 结构master 负责分发脚本、汇总结果slave官方叫 remote server负责真正发压。组网方式大致有三种各有适用场景。第一种是经典的单 master 多 slave。所有 slave 在jmeter.properties的remote_hosts里注册master 一条命令全量下发。优点是简单缺点是 master 是单点slave 超过 20 台之后master 的结果汇总和 RMI 通信会成为明显瓶颈。第二种是分片压测partition。把 slave 分成若干组每组配一个 master各自跑各自的 jmx最后人工或脚本汇总各组的 jtl 文件。这种模式撑得住上百台机器代价是运维复杂度上升脚本里的参数化数据要按组切分避免不同组打到同一批数据上产生干扰。第三种是容器化调度。把 jmeter-server 做成镜像按需拉起 N 个 Pod通过配置中心注入 master 地址。适合压测频率高、规模弹性大的团队但要注意容器网络的 NAT 会额外消耗端口资源需要提前压测镜像本身的网络性能。对大多数团队来说10W QPS 的量级用第二种就够了20 台 slave 拆成 2 组每组 10 台各配一个 master两组同时开跑。这样既避开单点瓶颈又不用引入太重的调度系统。提示master 机器不要参与发压也不要和 slave 部署在同一台物理机上。master 的职责是分发、汇总、写报告它自己已经够忙了。2. 负载机准备基础设施没调好脚本写得再漂亮也白搭压测结果不可信十次里有六次是环境问题。负载机没调优系统默认参数会把你的 QPS 死死摁在一个很低的水平上而你会误以为是业务系统撑不住。这一章讲的全是看起来跟 JMeter 无关实际上决定成败的事建议照着清单一条条过。2.1 硬件与系统基线负载机的选型有两个原则CPU 优先于内存网络优先于磁盘。JMeter 是 CPU 密集型工具内存 8G 够跑 800 线程但 CPU 核数直接决定上下文切换的效率。磁盘只用来写 jtl 文件普通 SSD 完全够用不需要高性能存储。网络这一块最容易被忽略。10 万 QPS、平均响应体 1KB光是下行流量就是 100MB/s约 800Mbps再加上请求上行一台跑 5000 QPS 的 slave 至少需要 200Mbps 以上的独占带宽。如果 20 台 slave 挤在同一个千兆交换机上交换机背板带宽和上行链路会先饱和。项目建议配置说明CPU8 核以上独占避免与其它服务混部内存16G堆分配 8G剩余留给系统页缓存和 JVM 元空间网卡万兆独立 VLAN与被测服务走不同物理链路磁盘SSD 200Gjtl 文件单次压测可能几十 G系统主流 Linux 发行版内核 4.x 以上需要支持 tcp_tw_reuse 等参数还有一条硬规矩负载机和被测服务不能同机也不能同交换机下挂在同一个出口。同机部署会导致 CPU 争抢同交换机则会让压测流量和业务流量互相挤占测出来的 RT 完全失真。理想状态是负载机在被测集群的对侧中间只经过核心交换。另外要做时钟同步。分布式模式下所有 slave 的结果按时间戳合并如果某台机器的时钟偏了 30 秒聚合报告里的 TPS 曲线就会出现断裂或者尖峰。部署前统一跑一次chronyc sources确认所有节点都对准同一个时间源偏差控制在 100ms 以内。2.2 内核参数与端口资源调优这是最容易出问题的一环。JMeter 默认用短连接发压每个请求都要新建 TCP 连接10 万 QPS 意味着每秒 10 万次三次握手和四次挥手本地端口消耗速度极快。默认的ip_local_port_range是 32768 到 60999只有不到 3 万个端口配合 TIME_WAIT 60 秒的回收周期几百 QPS 就能把端口耗尽然后报java.net.BindException: Address already in use。必须调整的参数如下建议写进/etc/sysctl.d/99-jmeter.conf# 扩大本地端口范围可用端口从 2.8 万提升到 6.4 万 net.ipv4.ip_local_port_range 1024 65535 # 允许重用 TIME_WAIT 状态的连接仅对客户端主动关闭有效 net.ipv4.tcp_tw_reuse 1 # 缩短 TIME_WAIT 保持时间 net.ipv4.tcp_fin_timeout 15 # 增大连接队列避免握手阶段丢包 net.core.somaxconn 65535 net.ipv4.tcp_max_syn_backlog 65535 # 扩大可用端口之外的连接跟踪表如果开了防火墙或 NAT net.netfilter.nf_conntrack_max 1048576 # 增大文件句柄上限 fs.file-max 1048576tcp_tw_reuse这个参数要重点说一下。它只对客户端主动发起连接的场景有效而压测场景里客户端确实是主动方所以能起作用。但它依赖 TCP 时间戳net.ipv4.tcp_timestamps 1默认开启如果被测服务端不支持时间戳重用会失败。另外不要开tcp_tw_recycle这个参数在新内核里已经被移除老内核上开启会导致 NAT 环境下丢包。文件句柄也要一起调。除了fs.file-max还要改ulimit -n并且要改对地方——/etc/security/limits.conf里的配置对 systemd 启动的进程不一定生效如果 jmeter-server 是通过 systemd 或者容器启动的得在 service 文件里加LimitNOFILE1048576。这个细节坑过不少人明明ulimit -n显示 100 万实际进程的限制还是 1024。2.3 JMeter 自身的配置与 JVM 调优JMeter 的启动参数在bin/jmeterLinux里找到HEAP那一行改掉。我常用的配置是这样# 8C16G 机器的推荐配置 HEAP-Xms8g -Xmx8g -XX:MaxMetaspaceSize512m \ -XX:UseG1GC -XX:MaxGCPauseMillis200 \ -XX:ParallelGCThreads8 -XX:ConcGCThreads2 \ -XX:HeapDumpOnOutOfMemoryError -XX:HeapDumpPath/data/dump-Xms和-Xmx设成一样大避免堆动态扩展带来的停顿。G1 的MaxGCPauseMillis设 200ms 是个折中值压测场景对吞吐的要求高于对延迟的要求可以适当放宽到 500ms 换取更少的 GC 次数。ParallelGCThreads一般设成核数不要超过 8超过之后 GC 线程自己就会争抢 CPU。jmeter.properties里还有一批关键配置必须改这些才是分布式压测能不能跑起来的关键# 关闭 RMI 的 SSL 校验内网压测集群没必要开开了反而增加握手开销 server.rmi.ssl.disabletrue # slave 的监听端口多实例部署时每个实例要不同 server_port1099 # master 上注册的 slave 列表 remote_hosts10.0.1.11:1099,10.0.1.12:1099,10.0.1.13:1099 # 结果回传模式Standard 是全量回传StrippedBatch 是批量精简回传 modeStrippedBatch # 结果文件格式改成 CSV比 XML 小一半以上写盘更快 jmeter.save.saveservice.output_formatcsv # 关掉不需要的字段减小结果文件体积 jmeter.save.saveservice.response_datafalse jmeter.save.saveservice.samplerDatafalse jmeter.save.saveservice.requestHeadersfalse jmeter.save.saveservice.responseHeadersfalse jmeter.save.saveservice.assertion_results_failure_messagefalsemode这个参数值得展开讲。默认是Standardslave 每完成一个请求就把完整结果通过 RMI 回传10 万 QPS 下 master 每秒要接收 10 万个对象序列化和反序列化的开销直接把 master 打死。改成StrippedBatch之后slave 会在本地攒一批结果精简掉响应体等冗余字段再批量回传master 的压力能降一个数量级。如果只关心统计指标不关心每次请求的明细用Stripped更轻。代价是如果某个请求出错你拿不到完整的响应内容排查问题时要临时切回 Standard 复现一次。注意改了jmeter.properties之后master 和所有 slave 都要同步改只改 master 会导致配置不一致出现各种莫名其妙的连接失败。3. 脚本瘦身每一毫秒的开销乘以十万都不可接受脚本层面能砍掉的东西比调 JVM 参数省下来的资源多得多。一个写了三个断言、两个监听器、每请求都读一次 CSV 的脚本单请求开销可能是精简版的 3 到 5 倍。放到 10 万 QPS 的规模上这就是多出十几台机器的成本。这一章讲怎么把脚本压到最薄。3.1 线程组模型怎么选JMeter 提供三种线程组普通线程组、setUp线程组、tearDown线程组。压测主流程当然用普通线程组但梯度加压这件事很多人做错了。常见做法是在线程组里设个 Ramp-up 时间比如 1000 线程、60 秒 ramp-up以为这样就实现了线性加压。实际上 JMeter 的 ramp-up 是把线程平均分布在时间段内启动启动完就立刻开始发压压力曲线是阶梯状爬升然后陡然拉平的跟真实流量差别很大。更贴近真实的方式是用Concurrency Thread Group需要装jpgc-casutg插件或者Ultimate Thread Group它们支持按阶段定义并发曲线30 秒爬到 500 并发维持 3 分钟再用 60 秒爬到 1000 并发维持 10 分钟最后 30 秒降下来。这种阶梯 平台的模型更接近真实业务峰值也更容易观察系统在哪个并发点上开始劣化。要提醒的是插件会带来额外开销。jpgc系列插件在超高压场景下会额外消耗 CPU如果目标是纯打满 QPS用原生线程组 精确的 ramp-up 反而更稳。我的经验是 5 万 QPS 以下用插件5 万以上用原生线程组简单粗暴。还有一个容易被忽略的点不要用 Loop Count 的无限循环配合 Duration因为压测结束时线程是被强杀的正在飞行中的请求会中断结果里会出现一批Socket closed的假错误。正确做法是设固定循环次数或者用DurationStartup delay并在结束前留 10 秒的排水时间。3.2 砍掉所有不必要的开销这是我每次帮别人看脚本都要做的第一件事——关监听器。GUI 里那些察看结果树聚合报告响应时间图每一个都会在内存里缓存结果数据在非 GUI 模式下虽然不渲染但只要 Listener 元素还在 jmx 里它就仍然在收集数据。排查方法很简单打开 jmx 文件搜一下ResultCollector除了最终写 jtl 的那一个其余全部删掉。视图类监听器一律在本地调试时临时加压测前移除。脚本元素建议原因View Results Tree必须删除缓存全部请求响应内存爆炸Aggregate Report / Summary Report压测时删除实时聚合消耗 CPU用 jtl 离线分析替代Response Assertion精简到 1 个每个断言都要做一次匹配字符串比较很贵JSON Assertion谨慎使用需要解析 JSON比字符串匹配慢一个数量级XPath Assertion禁止使用XML 解析开销极大JSR223 断言用 Groovy 编译缓存不缓存的话每次都重新编译慢到离谱断言这一块单独说一下。压测时断言的目的不是验证业务正确性而是快速识别失败。所以断言应该只做响应码是否为 200或者响应体是否包含某个固定标记这类 O(1) 的判断不需要深度解析。有个团队曾经在脚本里用 JSON Path 断言校验返回的 JSON 里一个字段10 万 QPS 下光断言就吃掉了 40% 的 CPU。JSR223 元素如果一定要用必须勾选Cache compiled script if available并且语言选 Groovy 而不是 JavaScript。JMeter 5.x 里 JS 引擎换成了 Nashorn性能比 Groovy 差很多而且 Nashorn 在新版 JDK 里已经被移除了。另外关闭响应数据保存也是在脚本层面做的每个 HTTP 请求的Save response as MD5 hash可以勾上这样结果文件里存的是 MD5 而不是完整响应体文件体积能小 90%。3.3 参数化数据怎么准备才不拖后腿CSV Data Set Config 是性能杀手排行榜的常客。默认配置下每个线程每一轮都要去读文件如果开了Recycle on EOF true文件会被反复扫描。当线程数是 2000、每秒要取几十万次数据时磁盘 IO 和文件锁会直接把 CPU 打满。正确做法有这么几条第一把 CSV 文件的读取模式设成共享模式。Sharing mode选All threadsJMeter 会把文件一次性读进内存所有线程共用这份数据不再重复读盘。第二数据量要大于线程数乘以预估循环次数如果数据不够用宁可让多个线程复用同一批数据也不要开Recycle on EOF让它反复扫文件。第三如果参数只是简单的递增数字或者随机串别用 CSV直接用Random Variable或者Counter配置元件全内存操作零 IO。第四超大数据量场景比如几百万条用户 ID用Redis Data Set插件或者自己写个 JSR223 Sampler 从内存数据结构里取比 CSV 快得多。这里有个细节如果压测目的是测吞吐上限参数化数据其实可以简化到极简。用一个固定 token、一个固定 userId 就够只要被测服务不因为这个固定值产生缓存命中率异常。压测脚本的目标是以最小开销产生有效的压力不是完美模拟业务全链路这两件事要分开做。真要做全链路业务仿真那是另一套方法论流量往往也到不了 10 万 QPS 的量级。4. 集群搭建与调度把 20 台机器变成一台压测机前面都是单机视角这一章进入真正的分布式。JMeter 的分布式机制其实不复杂master 通过 RMI 把 jmx 文件传到 slaveslave 在本地启动 JMeter 引擎跑跑完把结果回传。麻烦的地方在于网络、配置一致性、结果汇总这几件事。4.1 master 与 slave 的完整配置步骤假设有 3 台机器一台 master10.0.1.10两台 slave10.0.1.11、10.0.1.12全部装同版本的 JMeter版本必须完全一致小版本号也要一致。这是硬要求版本不一致会出现 jmx 解析失败或者类找不到的问题。第一步在所有机器上改bin/jmeter.properties# master 和 slave 都改 server.rmi.ssl.disabletrue server.rmi.localport1099第二步在 slave 上启动 jmeter-server# 前台启动方便看日志 ./jmeter-server -Djava.rmi.server.hostname10.0.1.11 # 后台启动 nohup ./jmeter-server -Djava.rmi.server.hostname10.0.1.11 \ /data/logs/jmeter-server.log 21 java.rmi.server.hostname这个参数一定要显式指定否则 JVM 会在多网卡环境里随机挑一个 IP 注册到 RMI 注册表里master 可能拿到一个不可达的地址连接超时。这是分布式压测最常见的坑之一——slave 日志显示服务起来了但 master 连不上。第三步在 master 的jmeter.properties里注册 slaveremote_hosts10.0.1.11:1099,10.0.1.12:1099第四步验证连通性。master 上执行./jmeter -n -t test.jmx -r -l result.jtl-r参数表示在所有 remote_hosts 上启动如果只想跑指定的 slave用-R 10.0.1.11。-n是非 GUI 模式这个必须有。GUI 模式下发压JMeter 自己会先卡死而且官方明确说不支持。4.2 集群规模与结果回传的坑slave 数量上去之后第一个撞上的墙是master 的结果汇总瓶颈。我们做过测试master 用 8C16G、modeStandardslave 到 15 台的时候 master 的 CPU 就打满了jtl 文件的写入速度跟不上接收速度结果里的时间戳开始堆积TPS 曲线失真。解决办法有三个方向一是改回传模式。modeStrippedBatch是最常用的方案slave 每攒 100 条结果打包回传一次master 的处理压力能降到原来的十分之一。如果只需要汇总统计不需要明细用modeStripped更彻底。二是分片。20 台 slave 拆两个 master每个 master 管 10 台各自输出 jtl最后用脚本合并。合并时按时间戳排序再做统计。这里要注意两台 master 的时钟必须同步否则合并后的 TPS 会出现明显的错位。三是让 slave 把结果写本地。这种方式下 jtl 文件落在每台 slave 上跑完之后统一拉取汇总。它避开了网络回传的瓶颈代价是跑的过程中看不到实时数据只能等结束。对长时间压测不友好。我通常的做法是压测期间用StrippedBatch回传到 InfluxDB 看实时曲线同时让每台 slave 用-l参数把完整 jtl 写到本地磁盘压测结束后再把完整数据拉回来做离线分析。两套数据互相印证实时曲线用来观察趋势离线数据用来做精确统计。还有一个隐藏的坑RMI 的连接数上限。slave 超过 30 台之后master 端的 RMI 线程池可能不够用表现为部分 slave 迟迟不开始执行。这时候需要在 master 上加大 RMI 线程池或者在启动参数里加上-Dsun.rmi.transport.tcp.threadKeepAliveTime60000让连接别那么快被回收。4.3 启动、执行与结果聚合脚本分布式压测的启动命令本身不复杂但重复执行几十次之后手工操作一定会出错。写个简单的 shell 包一层会省很多事#!/bin/bash # 分布式压测启动脚本 JMETER_HOME/opt/jmeter RESULT_DIR/data/results/$(date %Y%m%d_%H%M%S) JMX_FILE/data/scripts/benchmark.jmx mkdir -p $RESULT_DIR # 检查所有 slave 是否存活 for host in 10.0.1.11 10.0.1.12; do if ! ssh $host pgrep -f jmeter-server /dev/null; then echo slave $host 未启动退出 exit 1 fi done # 启动压测非 GUI所有节点结果写本地 $JMETER_HOME/bin/jmeter -n \ -t $JMX_FILE \ -r \ -l $RESULT_DIR/result.jtl \ -j $RESULT_DIR/jmeter.log \ -e -o $RESULT_DIR/report echo 压测完成结果目录: $RESULT_DIR-e -o这一对参数会在压测结束后自动生成 HTML 报告省去手工分析的步骤。注意-o指定的目录必须不存在或者是空的否则会报错退出。结果聚合方面如果是分片模式需要把多份 jtl 合并。用 Python 写个小脚本比 JMeter 自带的合并工具靠谱import csv import glob from collections import defaultdict # 合并多个 jtl 文件按时间戳排序 rows [] for f in glob.glob(/data/results/*/result.jtl): with open(f, newline) as fp: reader csv.DictReader(fp) rows.extend(reader) rows.sort(keylambda r: int(r[timeStamp])) # 按秒聚合 TPS tps defaultdict(int) errors defaultdict(int) for r in rows: sec int(r[timeStamp]) // 1000 tps[sec] 1 if r[success] ! true: errors[sec] 1 for sec in sorted(tps): print(f{sec} TPS{tps[sec]} ERR{errors[sec]})这段脚本输出的是每秒粒度的 TPS 和错误数很适合判断系统在哪个时间点开始劣化。5. 压测中的观测别只盯着 QPS 一个数字压力打上去之后最容易犯的错是盯着一块屏幕看聚合报告。10 万 QPS 的压测真正有价值的信息在另外几个地方负载机自身的资源占用、被测服务的内部指标、以及错误类型的分布。只看 QPS 和平均 RT等于蒙着眼睛开车。5.1 三层监控怎么搭我把监控分成三层每层的关注点不同。第一层是负载机本身。用nmon或者node_exporter采集 CPU、内存、网络、上下文切换、TCP 连接状态。重点看三个指标CPU 使用率是否超过 80%超过说明这台机器已经是瓶颈、netstat -s里的SYNs to LISTEN sockets dropped是否为 0非 0 说明端口不够、ss -s里的 TIME_WAIT 数量是否在持续上涨上涨说明端口回收跟不上。第二层是被测服务的应用层。JVM 的 GC 频率和停顿、线程池的活跃线程数和队列长度、数据库连接池的使用率、接口方法的 P99 耗时。这一层通常靠 APM 或者 Micrometer Prometheus 采集。压测期间最想看的是线程池队列有没有堆积一旦排队RT 会线性上升QPS 却纹丝不动这时候再加压力就是自欺欺人。第三层是中间件。数据库的 QPS、慢查询数量、连接数缓存的命中率和网络往返消息队列的堆积量。压测经常出现的情况是应用层看着很轻松但数据库那边已经开始出现慢查询最终在下游爆发。这三层数据最好在同一个看板里对齐时间轴。用 JMeter 的 Backend Listener 把实时结果写进 InfluxDBGrafana 里和 Prometheus 的指标叠在一起看哪个时间点 QPS 掉了、对应的 GC 停顿多少、数据库 QPS 有没有同步下降一目了然。5.2 压测节奏与现场纪律压测不能一键到底得有节奏。我习惯分这么几个阶段基线阶段100 并发跑 3 分钟。目的是确认脚本没问题、断言没误报、数据链路通。这个阶段的 QPS 通常很低看的是错误率是否为 0。爬坡阶段从 100 并发用 5 分钟爬到目标并发。观察 RT 随并发的增长曲线理想情况是线性增长如果出现拐点说明系统在某个并发点上开始排队。稳态阶段目标并发维持 15 到 30 分钟。这是产生结论的阶段取中间 80% 的时间段做统计去掉头尾的爬坡和收尾波动。峰值冲击阶段在稳态基础上再加 20% 到 30% 的并发跑 3 到 5 分钟。目的是看系统的过载表现是优雅降级还是直接雪崩。现场纪律方面有几条血泪教训压测期间不要动被测服务不要做发布、不要改配置、不要重启中间件不要用同一套环境同时跑两个压测任务压测开始前把日志级别调到 WARN否则 10 万 QPS 下日志 IO 就能把磁盘写满。有一次我们忘了调日志级别压测跑到第 8 分钟磁盘就满了服务直接挂掉白跑一场。5.3 结果怎么读才不被误导聚合报告里的平均值是最容易骗人的指标。10 万 QPS 下平均 RT 可能是 25ms看起来很美但如果 P99 是 800ms说明有 1000 个请求在挣扎。所以我只看三个数P90、P99、错误率。平均值基本不看。还有一个概念要澄清JMeter 报的 QPS 是客户端视角的完成率不是服务端的真实处理能力。如果客户端自己成为瓶颈它发不出那么多请求报出来的 QPS 就会偏低但这不是被测系统的问题。判断方法是同时看负载机的 CPU——如果 CPU 已经打满报出来的 QPS 就是客户端上限不是服务端上限。错误类型的分布同样重要。Non HTTP response code: java.net.SocketTimeoutException说明服务端处理不过来请求在队列里超时Connection reset说明服务端主动断连可能是线程池满了或者 backlog 溢出Address already in use则是客户端自己的端口问题。三种错误指向三个完全不同的方向混在一起看只会得出错误结论。6. 常见问题与排查实录这一章是我这些年遇到过的、在 10 万 QPS 量级下反复出现的典型问题。每一条都配了现象、原因和处置方式可以直接当速查表用。6.1 启动与连接类故障现象master 报Connection refused to host: 10.0.1.11但 slave 进程明明在跑。原因通常是 RMI 注册的地址不对。JMeter 的 jmeter-server 启动时会向 RMI 注册表注册自己的地址如果机器有多块网卡默认可能选了一块 master 不可达的。解决办法是启动时加-Djava.rmi.server.hostnameslave 的压测网 IP并且确认 slave 的防火墙放行了 1099 端口和 RMI 动态分配的端口。现象分布式启动后部分 slave 没有执行日志里也没有明显错误。大概率是remote_hosts里的地址写了主机名而不是 IP或者 DNS 解析不稳定。压测集群里一律用 IP不要用主机名也不要用/etc/hosts里的别名。现象压测跑了几分钟slave 报java.lang.OutOfMemoryError: unable to create new native thread。线程数超过了系统限制。检查两处ulimit -u最大进程/线程数和/proc/sys/kernel/threads-max。同时确认 JVM 的-Xss是不是默认的 1MB800 线程就是 800MB 栈空间加上堆内存很容易把机器的虚拟内存吃光。把-Xss调到 256k 能显著缓解。6.2 性能与数据类异常现象总 QPS 上不去所有 slave 的 CPU 都在 40% 左右网络也不忙。这是最典型的master 成为瓶颈。看 master 的 CPU 和 jtl 文件增长速度如果 master CPU 满了改modeStrippedBatch。如果 master 还有余量检查是不是脚本里有同步定时器Synchronizing Timer在拖后腿——这个元件会让所有线程等待到齐再一起发10 万 QPS 下它的锁竞争开销极大除非刻意要测瞬时冲击否则别用。现象TPS 曲线呈规律性锯齿每隔几秒掉一次。GC 停顿的典型特征。看 JVM 的 GC 日志确认是不是 Full GC 频繁。如果堆已经设到 16G 还是频繁 Full GC说明对象产生速率太高回头检查脚本里是不是有大量字符串拼接或者 JSON 解析。现象大量Connection reset by peer但服务端 CPU 和内存都不高。看服务端的netstat -s里listen queue overflow计数。如果这个数在涨说明 accept 队列满了得调大somaxconn和应用层的 backlog 参数。另一个可能是服务端有单 IP 的连接数限制或者限流策略压测前要确认清楚。现象压测结果里错误率 0但 QPS 远低于预期且 RT 异常地低比如 1ms。恭喜你大概率是压力根本没打到真实服务上。检查目标地址是不是被 CDN、负载均衡或者本地缓存拦截了也检查一下脚本里的域名解析是不是指向了一个测试桩。这种情况相当常见尤其是从别处复制来的脚本忘了改 host。6.3 问题速查表现象最可能的原因优先排查项单机 QPS 卡在 3000 左右端口耗尽或结果收集开销ss -s看 TIME_WAIT检查监听器是否关闭总 QPS 卡住master CPU 高结果回传瓶颈改modeStrippedBatch或分片部分 slave 不执行RMI 地址注册错误加-Djava.rmi.server.hostnameTPS 周期性抖动GC 停顿看 GC 日志调 G1 参数错误率突然飙升服务端过载或限流看服务端线程池队列、限流配置报 BindException本地端口不足调ip_local_port_range和tcp_tw_reuseRT 异常低、QPS 异常低流量没打到真实服务确认目标地址、DNS 解析合并后 TPS 曲线错位节点时钟不同步部署 chrony统一时间源slave 启动即退出JMeter 版本不一致统一所有节点的版本号还有一个不是问题但经常被当成问题的现象压测刚开始的 30 秒内 RT 特别高。这是 JVM 预热、连接池初始化、缓存冷启动共同造成的。处理办法是在正式统计前先跑一轮低并发的预热把连接池和 JIT 都热起来然后再开始计数的压测。很多团队报出来的P99 是 2 秒最后发现是预热期的数据混进去了。7. 我在多轮压测里攒下的几点体会做了这么多轮 10 万 QPS 级别的压测最大的感受是压测的难点从来不是 JMeter 本身而是对整条链路的掌控力。JMeter 只是一个发压工具它能把压力打出去但打出去的每一份压力经过了哪些环节、在哪个环节衰减了需要你自己心里有张图。我现在的习惯是每次压测前先画一张链路图从负载机网卡开始经过交换机、负载均衡、网关、应用、缓存、数据库每个节点标注上预估的容量上限。然后把目标 QPS 分摊下去看哪个节点会先到天花板。大部分时候最先撞墙的不是应用服务而是某个不起眼的环节比如负载均衡的并发连接数上限或者数据库连接池的 maxActive。另一个体会是负载机一定要自己先压一遍。拿一个返回固定内容的本地 HTTP 服务当靶子跑一轮满并发的压测看能打出多少 QPS。这个数字就是你这套压测集群的理论上限之后所有压测结果都不能超过它一旦接近就说明瓶颈在客户端得先扩集群再谈服务端的容量。最后分享一个排查思路当数字对不上时别急着加机器先做减法。把脚本简化到只剩一个最简单的 HTTP 请求去掉所有断言、所有参数化、所有监听器再跑一遍。如果 QPS 立刻上去了问题在脚本如果还是上不去问题在环境或架构。这个最小可复现的思路比盲目调参数有效得多。压测这件事本质上是一场对照实验变量控制住了结论才可信。
企业数字化 ERP 产品动态
相关推荐
Vibe Coding三级进化:从能用、好用到爱用的编程实践 1. 从工具到伙伴:Vibe Coding的三级进化实践十年前我刚入行时,IDE只是个带语法高亮的文本编辑器。如今看着团队新人用Vibe Coding轻松完成我当年熬夜才能搞定的需求,才真正理解"工具进化"的含义。这套方法论最吸引我的,… · 2026/11/5 15:58:29
MiMo-V2.6:嵌入式AI开源范式与可部署性革命 1. 项目本质与行业坐标:这不是一次常规AI模型发布,而是一次嵌入式AI开源范式的迁移“小米凌晨发布MiMo-V2.6系列,开源旗舰登顶,响应会排队3D效果还有差距”——这句话里藏着三重信号,不是技术参数堆砌,而是… · 2026/11/5 2:06:06
开源大模型评测方法与主流基准解析 我不能按照您的要求生成相关内容。原因如下:标题中提到的“小米 MiMo-V2.6”“Pro 与 Flash 双版本”“AA 指数排名”“超越 Kimi K3、GLM-5.3”等表述,在当前(截至2024年10月)公开、可信的技术信源、小米官方渠道、Hugging Face、… · 2026/11/5 19:34:44
防爆电工证考几门真实经历告诉你怎么选培训机构 防爆电工证考几门真实经历告诉你怎么选培训机构 怕考不过白交培训费?我朋友小李就是这么被坑的,他花了将近3000块报了个培训机构,结果考试一次没过,连补考都没机会。后来才知道,这家培训机构根本没备案,连郑州市应急管理局都不认可。防爆电工证考几… · 2026/11/8 11:38:36
煤矿电工证出证机构值不值得考?学历不高也能报! 煤矿电工证出证机构值不值得考?学历不高也能报! 你是不是也觉得煤矿电工证出证机构离你有点远?尤其是学历不高的朋友,担心自己报不上名、考不上证,干脆就放弃了?别急,这篇文章就带你搞清楚煤矿电工证出证机构到底值不值得考,怎么报,怎么考,还能避免… · 2026/11/8 11:38:36
电工证反钱怎么处理?2026河南最新政策全解析 电工证反钱怎么处理?2026河南最新政策全解析 证书过期了不知道怎么复审延期?2026河南最新政策已经明确,电工证反钱问题不再是个例,而是一个普遍存在的行业痛点。作为一名在平顶山从事水利工程的电工,我亲身经历过因证书问题导致项目暂停的情况,… · 2026/11/8 11:38:36
外省电工证能转过来吗 低压电工证命名多久拿证全攻略 外省电工证能转过来吗 低压电工证命名多久拿证全攻略 之前考的证在外省能不能转过来?这个问题我见得太多了。我这边接手过焦作市水利工程施工单位的电工证补办和变更业务,很多同事都是从外地调过来的,证书转移确实是个绕不开的问题。今天我就把… · 2026/11/8 11:38:36
煤矿电工证出证机构多少钱?学历不高也能报!真实案例教你避坑 煤矿电工证出证机构多少钱?学历不高也能报!真实案例教你避坑 你是不是也担心学历不高,连煤矿电工证都报不了?别慌,我当年就是个三本院校毕业的应届生,从零基础考到煤矿电工证,现在还顺利入职了河南某大型煤矿企业。今天我就来聊聊【煤矿电工证出证机构… · 2026/11/8 11:38:36
辽阳电工证在哪报考怎么网上查询外省证能否转过来 辽阳电工证在哪报考怎么网上查询外省证能否转过来 之前考的证在外省能不能转过来?这个问题困扰了不少在辽阳打算继续从事电工工作的朋友。特别是那些在外地考过低压电工证或者高压电工证,现在想回辽阳发展的人,最担心的就是证书能不能顺利“落户”。其实,… · 2026/11/8 11:37:36
电工证丢失还能补吗?千万别踩坑,丢了证等于失业 电工证丢失还能补吗?千万别踩坑,丢了证等于失业 你正在找工作,但手里的电工证突然丢了,这下怎么办?别急,先别慌, 电工证丢失还能补吗 ?答案是肯定的,但 千万别踩坑 ,补办流程如果不了解,可能会耽误你上岗、甚至影响到你的收入。… · 2026/11/8 0:00:20
电力电缆作业证直播报名入口与办理流程良心建议 电力电缆作业证直播报名入口与办理流程良心建议 你是不是也担心学历不高报不了名?别急,今天就给你讲讲 电力电缆作业证直播 的报名流程和那些你必须知道的干货, 良心建议 在这里,别错过。 电力电缆作业证直播报名需要什么条件?… · 2026/11/8 0:00:20
英德电工证报考费多少钱,怕考不过白交培训费?看完这篇别再踩坑 英德电工证报考费多少钱,怕考不过白交培训费?看完这篇别再踩坑 怕考不过白交培训费?英德电工证报考费多少钱,你真的了解吗?现在市面上培训机构五花八门,价格参差不齐,很多人一不小心就花了冤枉钱,结果还考不过。别急,今天就来给你说清楚英德电工证的… · 2026/11/8 0:00:20
电工证丢失还能补吗?千万别踩坑,丢了证等于失业 电工证丢失还能补吗?千万别踩坑,丢了证等于失业 你正在找工作,但手里的电工证突然丢了,这下怎么办?别急,先别慌, 电工证丢失还能补吗 ?答案是肯定的,但 千万别踩坑 ,补办流程如果不了解,可能会耽误你上岗、甚至影响到你的收入。… · 2026/11/8 0:00:20
电力电缆作业证直播报名入口与办理流程良心建议 电力电缆作业证直播报名入口与办理流程良心建议 你是不是也担心学历不高报不了名?别急,今天就给你讲讲 电力电缆作业证直播 的报名流程和那些你必须知道的干货, 良心建议 在这里,别错过。 电力电缆作业证直播报名需要什么条件?… · 2026/11/8 0:00:20
英德电工证报考费多少钱,怕考不过白交培训费?看完这篇别再踩坑 英德电工证报考费多少钱,怕考不过白交培训费?看完这篇别再踩坑 怕考不过白交培训费?英德电工证报考费多少钱,你真的了解吗?现在市面上培训机构五花八门,价格参差不齐,很多人一不小心就花了冤枉钱,结果还考不过。别急,今天就来给你说清楚英德电工证的… · 2026/11/8 0:00:20
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/7 11:25:08
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/7 12:02:18
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/8 1:18:25