“我加了 sleep,应该很温和了”——如果有 20 个 worker 各自 sleep,对方看到的是 143.7 个/秒。这篇用服务器记录的请求到达时间,把几种限速写法打出的真实节奏量出来:每个 worker 各自睡、全局固定间隔、令牌桶(不同桶容量),以及间隔从完成还是从开始算起。
1 个 worker:100 个请求用了 15.36 秒,平均 6.4 个/秒;相邻请求最小间隔 151.1 ms,中位 155.0 ms;任意 1 秒内最多 7 个,任意 0.1 秒内最多 1 个 5 个 worker:100 个请求用了 2.93 秒,平均 33.8 个/秒;相邻请求最小间隔 0.0 ms,中位 1.1 ms;任意 1 秒内最多 35 个,任意 0.1 秒内最多 5 个 20 个 worker:200 个请求用了 1.39 秒,平均 143.7 个/秒;相邻请求最小间隔 0.0 ms,中位 0.1 ms;任意 1 秒内最多 140 个,任意 0.1 秒内最多 20 个
在每个 worker 里请求之间 sleep(0.1),速率随 worker 数线性增长:5 个 worker 33.8 个/秒,20 个 143.7 个/秒(1 个 worker 只有 6.4 个/秒,因为还要加上请求耗时)。要限制对一个站点的总速率,limiter 必须是全局共享的。
1 个 worker:60 个请求用了 5.90 秒,平均 10.0 个/秒;相邻请求最小间隔 98.9 ms,中位 100.0 ms;任意 1 秒内最多 11 个,任意 0.1 秒内最多 2 个 5 个 worker:60 个请求用了 5.90 秒,平均 10.0 个/秒;相邻请求最小间隔 96.9 ms,中位 100.0 ms;任意 1 秒内最多 11 个,任意 0.1 秒内最多 2 个 20 个 worker:60 个请求用了 5.90 秒,平均 10.0 个/秒;相邻请求最小间隔 98.9 ms,中位 100.0 ms;任意 1 秒内最多 11 个,任意 0.1 秒内最多 2 个
用一把锁让所有请求排队,任意两个请求的开始至少隔 0.1 秒,速率恒定 10.0 个/秒,和 worker 数无关,最小间隔约 100 ms。
burst= 1:100 个请求用了 9.99 秒,平均 9.9 个/秒;相邻请求最小间隔 99.5 ms,中位 101.0 ms;任意 1 秒内最多 10 个,任意 0.1 秒内最多 2 个 burst= 5:100 个请求用了 9.50 秒,平均 10.4 个/秒;相邻请求最小间隔 0.1 ms,中位 100.0 ms;任意 1 秒内最多 15 个,任意 0.1 秒内最多 6 个 burst=20:100 个请求用了 8.00 秒,平均 12.4 个/秒;相邻请求最小间隔 0.0 ms,中位 99.8 ms;任意 1 秒内最多 30 个,任意 0.1 秒内最多 21 个
令牌桶限制的是平均速率,同时允许桶容量那么大的突发。burst=1 时和固定间隔几乎一样;burst=5 时 0.1 秒内最多 6 个;burst=20 时 0.1 秒内最多 21 个,整体平均速率也高于设定的 10 个/秒(12.4 个/秒,因为开头的突发)。对对方服务器友好,通常把桶设小。
第一批(桶是满的):前 0.5 秒内 15 个,前 0.1 秒内 11 个,30 个用了 2.00 秒 第二批(空闲 2 秒之后桶又满了):前 0.5 秒内 15 个,前 0.1 秒内 11 个,30 个用了 1.99 秒
桶在空闲期间会装满:停 2 秒后再发,前 0.1 秒内又出现 11 个请求。“平均速率受限”不等于“任何时刻都平稳”。
服务器耗时 0.05 秒:完成后再等 0.1 秒 → 平均 6.4 个/秒(相邻开始间隔中位 156 ms);开始到开始隔 0.1 秒(允许重叠)→ 平均 10.0 个/秒(中位 100 ms) 服务器耗时 0.5 秒:完成后再等 0.1 秒 → 平均 1.7 个/秒(相邻开始间隔中位 606 ms);开始到开始隔 0.1 秒(允许重叠)→ 平均 10.0 个/秒(中位 100 ms)
每个请求完成后再等一段时间,速率取决于对方的响应速度(0.05 秒时 6.4 个/秒,0.5 秒时 1.7 个/秒);按开始时间隔开,才能稳定地控制发出速率,并允许请求重叠。哪种更合适取决于你想保护的是对方的什么——这里只量了速率,没有量对方的负载。