在采集管道上线生产环境之前,合理的做法是有人问一句:满负荷运行时它是否能撑住。本能反应是做一次负载测试,这个本能是对的,但代理负载测试有一个普通负载测试所没有的限制:你所施加流量的对象通常属于别人。
正是这一点重塑了整个测试过程。你测试的不是某个系统能否承受你的负载,而是在不对第三方发起未经通知的压力测试的前提下,刻画你自己管道的行为并找到你的上限。以下是正确的做法。
你实际在测试什么
把四个问题分开,因为它们需要不同的测试,而其中只有一个涉及真实目标。
你自己管道的容量。 你的工作进程、连接处理、解析和存储在饱和之前能承受多少并发请求。这测试的是你的代码和基础设施,可以在不触碰任何人网站的情况下完成。
并发下的代理路径行为。 随着在途请求数量增加,延迟和成功率如何变化,这是通过网关来测量的。这也基本上与特定目标无关。
目标的容忍度。 特定网站在限流或发出验证挑战之前能接受什么样的速度。这一项必须谨慎、渐进地进行,而不是用负载生成器去猛冲。
端到端吞吐量。 上述所有因素共同产生的结果,这才是你的日程安排真正依赖的数字。
把这些混为一谈会产生一个典型的糟糕结果:一次”负载测试”猛攻某个目标,结果被封,除了告诉你”你可能会被封”之外什么都学不到。
先针对你自己拥有的东西测试
搭建一个你能控制的目标,让它返回一个大小真实的响应,然后让管道通过代理访问它。这样可以把第三方之外的所有因素隔离出来。
在这里你能学到出乎意料多的东西。你的工作进程池是否真的达到了你配置的并发数。连接处理是否按你想的那样工作,因为通过网关的连接池行为会有所不同,参见IP not rotating。你的解析或存储在哪里成为瓶颈。以及代理路径本身增加了什么样的延迟分布,而不掺杂目标自身的变化性。
使用真实的负载大小。针对一个极小的端点做测试会严重高估你的吞吐量,因为带宽和解析都随响应大小而变化,而住宅延迟在不同负载大小下的主导方式也不同。
测量正确的指标
单看吞吐量是不够的。五项测量能让负载测试变得可解读。
经过验证的成功率,而不是 HTTP 成功率。一个报告 100% 成功却返回验证挑战页面的测试,测量的是错误的东西,参见detecting blocked or fake content。
延迟百分位数,具体是 p50、p95 和 p99。住宅延迟分布很宽,所以平均值会掩盖真正决定一个有时间限制的任务能否完成的尾部情况。
吞吐量作为并发数的函数,应以曲线形式呈现,而不是只在某一点采样。有意思的地方在于曲线趋平的位置,因为超过那一点后,你增加的只是在途请求数量,而不是完成数量。
错误类别分布,因为这个组合能告诉你到底是什么在限制你。超时指向一个方向,速率信号指向另一个方向,验证挑战又是另一个方向,参见common residential proxy errors。
每次请求的字节数,因为它决定了生产环境下的成本,现在测量很容易,以后再发现代价会很大,参见estimating monthly bandwidth。
逐步爬升,不要猛冲
分阶梯逐步增加并发数,每个阶梯保持足够长的时间让数据稳定下来,并在每个层级记录完整的一组测量数据。突刺测试在这里几乎没什么用,因为它产生的失败与目标对突发流量的反应无法区分。
你要找的形状是:吞吐量不再随并发数上升而增长,同时 p95 延迟开始急剧攀升。这两者通常是同时出现的,标志着你的实际上限。让你的生产任务运行在这个上限之下,而不是刚好卡在上面,因为这个上限会随目标行为、一天中的时段和代理池状况而变化。
for concurrency in [5, 10, 20, 40, 80]: stats = run_step(concurrency, duration_seconds=180) # hold, then record print(concurrency, stats.validated_success, stats.p50, stats.p95, stats.rps, stats.bytes_per_req, stats.error_mix) if stats.validated_success < 0.9 or stats.p95 > LATENCY_BUDGET: break # found the knee谨慎地针对真实目标进行校准
最终你需要知道你的实际来源能容忍什么,而这是无法从模拟环境中学到的。把它当作校准来做,而不是负载测试。
从远低于你目标速率的水平开始,缓慢提升,关注经过验证的成功率而不是状态码。一旦出现任何退化迹象就停止提升,而不是一直推到失败,因为目标是找到一个可持续的速度,而不是一个崩溃点。校准的时间跨度要拉得比看起来必要的更长,因为限流常常是在滚动窗口上应用的,短暂的突发可能能通过,而持续的速率却不行。
要尊重目标给出的信号。429 或 Retry-After 是直接的指令,正确的回应是放慢速度,而不是切换地址来维持速度,这正是rate limiting and throttling中的区别所在。
并且要保持比例适当:你的校准流量相对于该网站的正常负载应该是可以忽略的误差。刻意对第三方基础设施进行压力测试并不是一种无害的工程实践,视规模和意图而定,它可能会演变成干扰行为。如果你需要精确知道合作伙伴的限度,直接询问他们。
解读结果
把曲线转化为你的生产配置所需要的两个数字:每个目标的并发上限和每个目标的速率,两者都应设定在拐点之下并留有余量。
然后对照你的日程做合理性检查。并发数是由速率和延迟推导出来的,所以如果你测得的 p50 延迟高于你原先的假设,那么在给定截止时间下所需的并发数也会相应上升,这正是planning request volume and concurrency中的算法。
最后,把这些结果当作会过期的东西对待。目标的防御手段会变化,代理池状况会因时段和市场而异,在一个平静周测得的数字在高峰期未必成立。定期重新进行校准,更重要的是,在生产环境中持续进行同样的测量,让上限是被观测到的,而不是被假设出来的,参见monitoring proxy health at scale。
结论
代理负载测试是容量刻画,不是对第三方的攻击。把四个问题分开,把前两个针对你自己拥有的基础设施用真实的负载大小进行测试,这样可以把你管道的真实瓶颈从目标的变化性中隔离出来。测量经过验证的成功率、延迟百分位数、吞吐量随并发数的变化、错误类别组合和每次请求的字节数,然后分阶梯爬升,寻找吞吐量趋平、p95 攀升的拐点。针对真实目标缓慢校准,一旦出现退化就停止,而不是推到失败,遵守速率信号,选择放慢速度而不是切换地址。把生产上限设定在拐点之下,根据实测延迟重新推导你的并发数,并持续在生产环境中测量,因为上限会移动。
被测试的路径是residential proxies,其中并发数和地理位置是按请求设置的参数,而不是套餐设置,按每 GB计费,因此一次范围恰当的负载测试所花费的成本大致就是其带宽本身的成本。