压测的目标数值从哪里来
目标
从 4 周的按小时流量数据出发,依次应用日总量、峰值时段、安全系数、故障余量和增长率,推导出压力测试的目标 RPS,并进一步确认这个目标在本环境中能否真正施加出来。
为什么重要
压力测试计划书中最常空缺的一栏,是目标 RPS 的来源。如果问“每秒 1,000 次”是从哪里来的,通常没有答案,这样即使测试通过,也说不清保证了什么。目标必须从生产数据中推导出来。把一天的总量除以 86400 得到的平均值,几乎总是比实际峰值小好几倍,如果按这个平均值来规划容量,就会恰好少掉这个倍数。再依次乘上:即使在峰值时也不用尽容量的余量(headroom)、即使一个可用区掉线也能承受的冗余,以及到下一个规划周期为止的增长率。最后,还必须确认按此确定的目标是否真的能施加出来——用没能施加出来的负载得到的通过,不是通过。
步骤
/opt/lab/lt/lt-capacity-target/traffic.tsv是 4 周的按小时请求数(一行表头之后是날짜 시각 요청수(韩文,依次意为日期、时刻、请求数))。把按日期的合计,以 28 行、每行以制表符分隔的两个字段<날짜> <하루 총 요청 수>(占位符依次为日期、每日请求总数)写入/root/lt-capacity-target/daily.tsv,并在/root/lt-capacity-target/01-peak-day.txt中写两行peak_date=和peak_total=。也就是最忙的那一天。- 在最忙那一天的 24 小时中,找出最忙的一个小时,在
/root/lt-capacity-target/02-peak-hour.txt中写三行:peak_hour=(0 到 23 的整数)、peak_hour_requests=(那一个小时的请求数)、peak_share=(那一个小时占当天总量的比例,保留到小数点后第四位)。 - 在
/root/lt-capacity-target/03-rps.txt中写三行。avg_rps=是最忙那天的总量除以 86400 所得的值,peak_rps=是最忙一个小时的请求数除以 3600 所得的值(两者都保留到小数点后第二位),peak_over_avg=是两者之比(保留到小数点后第二位)。 - 把运行余量(headroom)定为 30%——意思是即使在峰值时,也只使用容量的 70%。在
/root/lt-capacity-target/04-headroom.txt中写两行headroom=0.30和rps_after_headroom=。后一个值是peak_rps ÷ (1 − headroom),保留到小数点后第二位。 - 服务均匀部署在 4 个可用区,即使一个可用区整个掉线,也必须承受峰值。在
/root/lt-capacity-target/05-nplus1.txt中写三行nodes=4、surviving=3、rps_after_nplus1=。后一个值是rps_after_headroom × nodes ÷ surviving,保留到小数点后第二位。 - 流量每月增长 8%,并且要靠这个容量撑过 6 个月。在
/root/lt-capacity-target/06-growth.txt中写四行monthly_growth=0.08、months=6、growth_factor=(1.08 的 6 次方,保留到小数点后第四位)、rps_after_growth=(rps_after_nplus1 × growth_factor,保留到小数点后第二位)。 - 创建
/root/lt-capacity-target/target.tsv。没有表头,共五行,每行是以制表符分隔的两个字段<단계> <RPS>(占位符依次为阶段、RPS)。阶段名称依次为peak、headroom、nplus1、growth、target,前四行写前面几步写下的值,最后的target行写rps_after_growth向上取整后的整数。然后在/root/lt-capacity-target/07-note.txt中写两行test_target_rps=(那个整数)和reason=(用至少 60 个字符写明这个目标是从哪里来的)。 - 把
/opt/lab/lt/lt-capacity-target/echo.py启动在端口 8095 上,并把第 7 步确定的目标真正施加上去。worker 为 20 个,每个 worker 的速率上限是올림(목표 ÷ 20)(即目标除以 20 后向上取整),请求数是목표 × 5(即目标乘以 5),并把原始 CSV 保留在/root/lt-capacity-target/run.csv中。然后在/root/lt-capacity-target/08-run.txt中写五行target_rps=、workers=20、q_per_worker=、measured_rps=(根据 run.csv 重新计算的值,保留到小数点后第二位)、reached=(如果 measured 达到目标的 90% 以上则为yes,否则为no),并加上一行至少 50 个字符的note=。
参考
- 工作目录是
/root/lt-capacity-target。如果不存在,请先创建。 - 数据和压力对象在
/opt/lab/lt/lt-capacity-target/中——traffic.tsv(4 周 × 24 小时)、生成该数据的gen_traffic.py(固定了种子),以及第 8 步要用的echo.py。 - 四舍五入的约定:比例保留到小数点后第四位,RPS 保留到小数点后第二位,只有最后的目标是向上取整的整数。评分器会从数据中重新做同样的计算来比对。
- 常见错误:把余量当作乘法来应用。30% 的余量不是
× 0.7,而是÷ 0.7。 - 常见错误:用单利来计算增长率。每月 8%、6 个月,不是 1.48,而是 1.08 的 6 次方。
- Google SRE Workbook — Managing Load、SRE Book — Software Engineering in SRE(需求预测)、hey — 选项 -n、-c、-q 的定义、k6 — scenarios 与到达率、USE Method
先统计一天的总量
/opt/lab/lt/lt-capacity-target/traffic.tsv 是 4 周的按小时请求数(一行表头之后是 날짜 시각 요청수(韩文,依次意为日期、时刻、请求数))。把按日期的合计,以 28 行、每行以制表符分隔的两个字段 <날짜> <하루 총 요청 수>(占位符依次为日期、每日请求总数)写入 /root/lt-capacity-target/daily.tsv,并在 /root/lt-capacity-target/01-peak-day.txt 中写两行 peak_date= 和 peak_total=。也就是最忙的那一天。
用 awk -F'\t' 按日期累加第三个字段即可。请跳过表头那一行。容量规划的基准不是平均值,而是最忙的那一天——如果按平均值来定,那一天就会垮掉。
在这一天之内,什么时候最忙
在最忙那一天的 24 小时中,找出最忙的一个小时,在 /root/lt-capacity-target/02-peak-hour.txt 中写三行:peak_hour=(0 到 23 的整数)、peak_hour_requests=(那一个小时的请求数)、peak_share=(那一个小时占当天总量的比例,保留到小数点后第四位)。
如果 24 小时是均匀的,一个小时的份额是 1 ÷ 24 = 0.0417。这一步的要点是实际值是它的几倍——这个倍数,就是“按平均值规划会少多少”。
除以平均值,会少掉多少倍
在 /root/lt-capacity-target/03-rps.txt 中写三行。avg_rps= 是最忙那天的总量除以 86400 所得的值,peak_rps= 是最忙一个小时的请求数除以 3600 所得的值(两者都保留到小数点后第二位),peak_over_avg= 是两者之比(保留到小数点后第二位)。
一天的总量 ÷ 86400,是“如果那一天 24 小时始终一样忙”的值。实际峰值比它大得多。如果不知道这个倍数,就按平均值来定容量,在峰值时段恰好会少掉这个倍数。
留出余量,抬高目标
把运行余量(headroom)定为 30%——意思是即使在峰值时,也只使用容量的 70%。在 /root/lt-capacity-target/04-headroom.txt 中写两行 headroom=0.30 和 rps_after_headroom=。后一个值是 peak_rps ÷ (1 − headroom),保留到小数点后第二位。
如果在峰值时把容量用到 100%,就已经处在队列开始堆积的那个点上了。在排队论中,利用率越接近 1,等待时间就增长得越急剧。所以目标要定在峰值之上。这是除法——不是乘以 0.7。
让它在一个可用区掉线时也能承受
服务均匀部署在 4 个可用区,即使一个可用区整个掉线,也必须承受峰值。在 /root/lt-capacity-target/05-nplus1.txt 中写三行 nodes=4、surviving=3、rps_after_nplus1=。后一个值是 rps_after_headroom × nodes ÷ surviving,保留到小数点后第二位。
四个可用区只剩 3 个时,为了承受同样的负载,整体必须拥有 4/3 倍的容量。所以测试中需要确认的负载也要相应提高。如果漏掉这个系数,平时没有任何问题,只会在一个可用区掉线的那一天垮掉——这是最糟糕的失败方式。
半年之后这个目标还能用吗
流量每月增长 8%,并且要靠这个容量撑过 6 个月。在 /root/lt-capacity-target/06-growth.txt 中写四行 monthly_growth=0.08、months=6、growth_factor=(1.08 的 6 次方,保留到小数点后第四位)、rps_after_growth=(rps_after_nplus1 × growth_factor,保留到小数点后第二位)。
这是复利——不是 8% × 6 = 48%,而是 1.08 的 6 次方。请亲自算一算两者的差别会让目标数值改变百分之几。增长率原则上应该从数据中估计,但在这一步中,把它当作从产品规划中拿到的值,把注意力集中在计算方法上。
把目标的来源留在一个文件里
创建 /root/lt-capacity-target/target.tsv。没有表头,共五行,每行是以制表符分隔的两个字段 <단계> <RPS>(占位符依次为阶段、RPS)。阶段名称依次为 peak、headroom、nplus1、growth、target,前四行写前面几步写下的值,最后的 target 行写 rps_after_growth 向上取整后的整数。然后在 /root/lt-capacity-target/07-note.txt 中写两行 test_target_rps=(那个整数)和 reason=(用至少 60 个字符写明这个目标是从哪里来的)。
这一张表,就是对“每秒 1,000 次是从哪里来的”这个问题的回答。看这四行,还能一眼看出改变哪个假设,目标会移动多少——请在脑子里算一算,如果把余量降到 20%,目标会变成多少。
这个目标在本环境中究竟能不能施加出来
把 /opt/lab/lt/lt-capacity-target/echo.py 启动在端口 8095 上,并把第 7 步确定的目标真正施加上去。worker 为 20 个,每个 worker 的速率上限是 올림(목표 ÷ 20)(即目标除以 20 后向上取整),请求数是 목표 × 5(即目标乘以 5),并把原始 CSV 保留在 /root/lt-capacity-target/run.csv 中。然后在 /root/lt-capacity-target/08-run.txt 中写五行 target_rps=、workers=20、q_per_worker=、measured_rps=(根据 run.csv 重新计算的值,保留到小数点后第二位)、reached=(如果 measured 达到目标的 90% 以上则为 yes,否则为 no),并加上一行至少 50 个字符的 note=。
总 RPS 是 요청 수 ÷ (max(offset + response-time) − min(offset))(即请求数除以 max(offset + response-time) 与 min(offset) 之差)。如果没能施加出目标,那不是服务器的结论,而是生成器的局限,必须在报告中写明这一点——如果不写,下一个人就会把这个数字当作服务器的能力来读。