压测与容量评估:IM 融合前要扛多少流量?

1 分钟阅读
·

系列目录

  1. 从单线程到线程池:云盘转 Java 后的第一堂并发课
  2. 线程池不是 new 出来就完事:参数、队列与快慢接口隔离
  3. 数据库连接池与 Spring 声明式事务:把 Node.js 的坑填上
  4. ConcurrentHashMap 与锁:文件元数据的并发读写
  5. Future 与 CountDownLatch:一个接口聚合一堆下游
  6. 用 Kafka 做异步化与削峰:从热点上报到全局事务
  7. JVM 内存与 GC:一次线上 Full GC 排查记录
  8. 日志不是越多越好:logback 实践与线上问题定位
  9. 高性能 IO:流对流的失败率、NIO 与零拷贝
  10. 压测与容量评估:IM 融合前要扛多少流量?(本篇)

上一篇预告了压测。此前几篇中的压测只验证单个问题,例如第 2 篇用压测调整线程池参数,第 4 篇用曲线比较 ConcurrentHashMap 改造前后的表现。IM 融合需要评估整套服务的容量,压测需要覆盖接口组合、下游依赖和数据分布。

写 Java 服务前,我已有 Node.js、C# 等后端经验,预先判断线程池、连接池、锁、GC 和下游调用会在流量上升时相互影响。因此在项目推进前先补了并发与性能分析的知识,再把这些方法用于线程池参数、连接池和锁的验证。IM 融合将容量问题具体化:预期流量下,服务是否满足 RT 和错误率要求;单机可以持续承担多少 QPS;增加机器后如何保留故障余量。

压测要在预期 RT 分位数和错误率要求内,找出吞吐量开始受排队影响的区间。RT 开始加速上升的拐点提示资源接近饱和,但不表示固定的真实容量。容量估算可用目标 QPS 除以单机在目标服务水平内可持续承载的 QPS,再为环境偏差、故障冗余和业务波动预留余量。

IM 融合前需要回答的容量问题

IM 消息文件实现 介绍了背景:云盘要承接大象 IM 的全部文件流转,图片消息一天几千万条。文件下载链路后来移至 nginx+lua,第 9 篇分析过这条路径;鉴权、寻址、元数据等接口仍由 java 服务处理,大象的流量会进入云盘服务端。

方案评审提出的问题很直接:这套服务能承受多少流量,融合后是否足够。单机最大能到多少 QPS,RT 从何时开始劣化,流量翻倍需要增加几台机器,当时都没有数据可以回答。只能按保守的扩容数推进评审。

因此把完整压测排入计划,用测量结果支撑容量判断。本文记录当时学习、验证和使用结果的过程。

QPS、RT 与容量边界

补充学习时,先区分了拐点和峰值的用途。

QPS 与 RT 会相互影响。 在资源未饱和的区间,提高到达压力后,完成的 QPS 通常上升,RT 变化较小。某个资源接近饱和时,请求在该资源前排队,等待时间推高 RT。固定到达速率施压时,完成 QPS 可能进入平台;错误加重时,完成 QPS 还可能下降。因此需要同时记录施加压力、实际完成 QPS、RT 和错误率。RT 曲线开始加速上升的区间可称为拐点,它说明资源饱和和排队风险。第 4 篇中「RT 随并发线性上涨」的曲线,记录了锁竞争使系统更早进入排队区间的现象。

RT 要看分位数,也要保留平均值。 拐点附近,平均 RT 可能只小幅上升,而 P99 已经升高,因为少量请求等待很久。平均值无法表示尾部请求的等待时间,P99 也不能单独表示整体情况。容量判断要结合目标接口的平均 RT、RT 分位数和错误率。

超过服务目标后的峰值 QPS 不能用于容量规划。 持续加压到大量超时和错误率上升,可以观察故障区间的吞吐量;生产容量应以明确的 RT 分位数、错误率和压测持续时间为约束,在稳定区间内取值。拐点可用于确定余量大小。接口类型、数据分布和下游状态变化时,拐点也会变化。

QPS 与 RT 的关系曲线与拐点标注

当时使用公司内部压测平台。配置目标机器和接口后,平台按梯度加压,例如每档增加若干并发并稳压几分钟。平台输出实时 QPS、RT 分位数和错误率曲线,用于确认稳定区间和拐点。压测数据由我们构造,用户、目录树和文件按线上数据分布生成,读写比例参考线上监控。结果是否可靠,取决于数据分布和压测环境与生产的接近程度。

压测环境必须说明与生产的差异

第一版压测结束后,发现环境与生产有明显差异。这些差异会改变结果。

数据量。 测试库的数据量与生产相差几个数量级。数据量会影响索引深度、缓存页命中率和慢查询的触发概率。第 2 篇中「数据量上来之后从几十毫秒涨到秒级」的慢查询,在小数据量测试库中无法复现。当时尽量将测试库灌到与生产同量级。历史冷数据比例等无法覆盖的部分,按结果可能偏乐观处理,并在容量评估中留余量。

缓存命中率。 如果自造数据按均匀分布随机访问,热点形态会与线上不同。线上访问符合幂律,少数热点目录和文件承接大部分流量。均匀分布会使 tair 和本地缓存命中率偏低或偏高,取决于造数方式。当时两种偏差都遇到过。后来按线上访问日志回放用户和文件分布,命中率才接近线上监控。

下游真假。 下游使用 mock,或连接到负载较低的测试环境时,下游 RT 会低于生产时的表现。第 5 篇中的聚合接口受最慢下游影响。压测未覆盖对应状态时,结果只能表示服务在该下游条件下的拐点,无法推导端到端容量。能接入真实下游测试集群时应接入;无法接入时,可以单独压测下游取得基线。但不能只将基线延迟加回总 RT,因为并发、超时和排队会相互影响。这种结果需要明确只适用于当前下游条件。

处理这些差异后,结论仍受环境限制。压测结果可与上一次同条件压测比较,判断是否回归;也可用于定位线程池、连接池、锁或 GC 等瓶颈。容量评估则需要记录环境和数据分布差异,并为未覆盖部分预留余量。曲线上的数字不能脱离压测条件使用。

两个压测问题

压测中遇到两个需要纳入流程的问题。

下游测试环境被打挂。 第一次上量时,梯度增加过快,下游某个测试集群无法承载,影响了其他团队的测试。压测前没有通知下游,也没有确认对方环境的容量。后来规定,压测前通知链路上的所有下游并确认可接收的流量;每一档加压后检查下游监控,再进入下一档。

压测数据进入统计和用户可见区域。 造出的压测流量沿正常链路进入热点上报,第 6 篇中的 Kafka 链路,以及若干统计口径,造成统计偏差。部分脏数据进入用户可见区域,例如测试账号的配额计数和回收站。后来为压测账号和数据打标,在链路上隔离或丢弃打标流量;压测结束后运行清理脚本并验收。这些要求后来进入团队的压测 checklist,作为每次压测前的检查项。

容量评估表与扩容阈值

压测完成后,评审问题可以形成初版估算。下表保留当时的写法,数字全部脱敏,只示意量级:

# 容量估算方法(量级示意,非真实数字)
目标峰值 QPS = 日文件消息量 × 峰值系数 ÷ 86400      # 如:几千万条 × 3 ÷ 86400 ≈ 千级
单机安全 QPS = 单机拐点 QPS × 0.7                    # 拐点取自压测曲线,不打满
最少机器数   = ceil(目标峰值 QPS ÷ 单机安全 QPS)
实际部署     = 最少机器数 × 安全系数(1.5~2),且保证 N+1 冗余

这段计算式说明了当时的估算过程,不能直接作为完整部署计算式。目标峰值 QPS 还要按各接口的请求放大系数拆分。实际部署 中的安全系数和 N+1 也要分别计算,不能仅靠相乘后附加一句「保证 N+1 冗余」。这张表落地时确定了以下约定:

  • 单机安全 QPS 取拐点的七成左右。 这是当时选择的运行余量,不能直接套用。拐点后的 RT 已开始上升,容量表还要用目标 RT 分位数和错误率复核;
  • 峰值系数按线上监控中「峰值小时 ÷ 全天均值」估算,大象侧按消息量的昼夜分布再放大一档。该算法以前提是目标文件消息量可换算为目标接口请求量;读写比例、重试和一个消息触发多个请求时,需要单独折算;
  • 安全系数为 1.5 到 2,覆盖压测环境偏差和业务波动。当时同时使用安全系数和单机七成余量,实际部署后还要按单机故障时的剩余机器数复核 N+1 是否满足目标负载;
  • 扩容阈值随容量表调整。日常峰值接近「单机安全 QPS × 在线机器数」的某个比例时触发扩容评审,当时定为六成上下。六成是当时的触发线,需要随目标服务水平、扩容耗时和故障预留调整。

容量表不能在填完后固定。大版本上线,或修改线程池、连接池、JVM 等可能影响性能的参数后,都需要重新压测并更新结果。容量结论只适用于记录的版本、配置、数据和下游条件。

用压测校正前面的参数

这轮压测也用于回头验证此前按经验设置的参数:

  • 第 2 篇的线程池。 当时采用「先按经验拍,再按压测调」。压测显示快接口池的 RT 在预期之前升高。排查发现数据库连接先被占满,线程在等待连接,第 3 篇预告过这种联动。因此先调整连接池参数,并下调线程池参数。
  • 第 4 篇的锁改造。 ConcurrentHashMap 替换后,曲线上的拐点后移。该对比可作为这次改造在当时压测条件下的验收结果,不能推及所有生产场景。
  • 第 7 篇的 JVM 参数。 高压档下检查 GC 日志。拐点之前,Young GC 频率和停顿没有异常变化,因此 GC 不是该压测条件下拐点的首要原因。新生代调参的效果也有了量化记录。

前半段系列中的参数因此有了对应的测量记录。压测既用于估算特定条件下可承载的负载,也用于验证调优是否改变了瓶颈位置和服务指标。

当时没有覆盖的条件

有两项没有做到:没有条件做全链路、生产环境的压测,所有数字都来自测试环境并预留余量,误差范围没有验证;拐点测试只覆盖主要接口,长尾接口容量按类比估算。缓存命中率也是当时难以校准的变量,压测中始终无法稳定复现线上形态。下一篇继续记录缓存相关的问题。

参考资料

  • 《性能之巅:洞悉系统、企业与云计算》(Brendan Gregg)相关章节
  • 当时的压测报告与容量评估表(脱敏)

453 字 · 54 段落
ximing

Written by ximingFollow onGitHub

相关文章