压测工具要配合背压
一句话总结
如果负载测试结果和生产指标不同,通常应该怀疑负载测试方面。因为实际用户不会因为之前的请求很慢而推迟下一次请求。
为什么需要这个?
假设负载生成器设置为每秒1000件,但一个响应耗时2秒。同步式(closed)生成器在2秒内没有发送应该发送的2000件。系统最慢的区域的样本全部消失,负载生成器“配合”自己创造的背景预处理。Gil Tene称之为coordinated omission的问题。
症状有三个。即使提高负载,p99几乎也不动。报告的处理量低于设定的目标值,但延迟分布看起来和从目标值测量的样子一样。而且生产比负载测试还差得多。
所有执行都应应用的一项容纳性检查是,报告的实际处理量是否与设定的目标处理量一致。如果不一致,则该执行的延迟分布不可靠。
怎么行动
这就是closed模型和open模型的区别。closed固定同步性(总是N个正在处理)。open固定到达率(与是否响应无关,每秒λ个到达)。实际用户接近open。
小李的定律L = λ × W连接两个模型。系统内的平均请求数是到达率乘以平均停留时间。RPS 1,000,延迟100ms的情况下同时处理100个。相反,如果在将同步性设为10的closed运行中,RPS ×平均延迟不接近10,那就意味着负载生成器实际上没有达到目标负载。
连接池大小也照样使用。一个实例每秒500个请求,平均80ms,500×0.08=40,考虑到尾随延迟,再给1.5倍的余地的话,推荐的池大小是60。不能在这里停止。需要按舰队重新计算。实例40个×池60=最多2,400个连接,但Postgresmax_connections如果超过200,发行后连接量激增,会导致故障。
在现场相遇的样子
坏的基准有共同点。没有热身,JIT没有加热,没有固定CPU频率,没有考虑OS噪音,只测量一次,没有处理异常值。
好的程序很简单。热身10次,本测量100次以上,排除2σ以外,同时查看p50·p95·p99,记录环境。如果也知道冷启动实测值,有助于确定热身长度。Python/Node 150–500ms,Go/Rust 50–100ms,JVM 1000–3000ms,GraalVM Native 50–100ms。
测试种类也根据目的分为负载(在预期流量中正常运行)、压力(为了找到极限点逐渐增加)、峰值(急剧增加反应)、耐久性Soak(长时间检测内存泄漏)。
把梯子抬到哪里?
在提高同步性的同时,在烧炭的梯子上人们最常犯的错误是停得太早和停得太久。处理量还在上升,如果停下来,就无法找到极限,即使已经折断了,继续提高的话,考试本身也会破坏系统,后面的测量全部无法使用。
确定停止点标准有三个。
- 处理量不再上升。将同时性提高了两倍,但处理量增长不到10%就已经饱和了。这个点被称为膝盖。
- 开始出现错误。错误率上升后,之后的延迟数值就没有意义了。失败的请求通常很快结束,反而会产生看起来延迟好的错觉。
- 不会回来的。降低了负担,但延迟如果原样不回来,那就说明什么东西坏了。可能是积攒了能量,连接断裂,或者内存无法恢复。这个时候要停止考试,看看原因,不能继续爬梯子。
第三个其实是最有价值的观察。正常系统下负载时,延迟也会随之下降。否则,这意味着该系统在承受不了峰值时无法自行恢复,这比处理量数字更重要的事实。因此,在上升后,一定要下来一次再回来。上升时和下降时的同时性中,如果值不同,那么差异就是恢复能力的缺陷。
再说一点。每个阶段必须足够长。30秒的阶段无法观察到自动扩展、缓存预热和队列堆积。至少要保持几分钟才能出现正常状态的值,之前的值是过度区间,最好单独标明。特别是带有自动扩展的系统,新实例出现和准备的时间都是整个过度区间,因此在比那个时间短的阶段,甚至无法知道扩展是否实际有帮助。
下次实验要做的事情
计划并通过脚本自动执行同步性1·2·5·10·20梯子。将结果制成表格,根据规则寻找处理量不再增加的临界点,根据小力的定律验证执行本身是否有效,然后单独进行固定到达率的open执行,对比目标与实际处理量。