引言:
漏斗算法(Leaky Bucket),该算法有两种处理方式Traffic Shaping和Traffic Policing
在桶满水之后,常见的两种处理方式为:
将水看作网络通信中数据包的抽象,则方式1起到的效果称为Traffic Shaping,方式2起到的效果称为Traffic Policing
由此可见,Traffic Shaping的核心理念是"等待",Traffic Policing的核心理念是"丢弃"。它们是两种常见的流速控制方法
源码:
limit_req模块的源码在src/http/modules/ngx\_http\_limit\_req\_module.c,关于burst的核心代码在这。//src/http/modules/ngx_http_limit_req_module.c:396
ms = (ngx_msec_int_t) (now - lr->last);
excess = lr->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000;
if (excess < 0) {
excess = 0;
}
*ep = excess;
if ((ngx_uint_t) excess > limit->burst) {
return NGX_BUSY;
}
if (account) {
lr->excess = excess;
lr->last = now;
return NGX_OK;
}
excess初始值是0,假设现在ctx->rate是2000(即2 request/s),这次请求距离上次请求是400ms。
那么excess = 0 – 2000 * 400 / 1000 + 1000 = 200。如果limit->burst是0,那么200 > 0,会返回NGX_BUSY即是503了。
假如burst是1,limit->burst即是1000,那么如果请求是每隔400ms来一个,共需5个才会填满limit->burst(每个请求将会增加200 excess),到第6个才会返回503。
推导出公式,假设设置频率是r request/s,每次请求距离上次请求t ms,设置burst为b,
那么返回503的临界请求个数x是
\begin{equation}
x = floor(b * \frac {( 1000 / r )} {( 1000 / r – t )})
\end{equation}
虽然知道了源码但是还是没有一个非常直观的感觉,所以不如实际操作一下:
实际操作:
nginx中该模块的使用配置示例l
imit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;
server {
location /search/ {
limit_req zone=one burst=5 nodelay;
}
第一段配置参数:
第二段配置参数:
这里的burst参数主要采用了令牌桶算法。令牌桶算法是网络流量整形(Traffic Shaping)和速率限制(Rate Limiting)中最常用的一种算法。典型情况下,令牌桶算法用来控制发送到网络上的数据数目,并允许突发数据的发送。
令牌桶这种控制机制基于令牌桶中是否存在令牌来指示什么时候可以发送流量。令牌桶中的每一个令牌都代表一个字节。如果令牌桶中存在令牌,则允许发送流量;如果令牌桶中不存在令牌,则不允许发送流量。因此,如果突发门限被合理地配置并且令牌桶中有足够的令牌,那么流量就可以以峰值速率发送。令牌桶算法的基本过程如下:
注意:令牌桶算法不能与另外一种常见算法“漏斗算法(Leaky Bucket)”相混淆。这两种算法的主要区别在于“漏斗算法”能够强行限制数据的传输速率,而“令牌桶算法”在能够限制数据的平均传输数据外,还允许某种程度的突发传输。在“令牌桶算法”中,只要令牌桶中存在令牌,那么就允许突发地传输数据直到达到用户配置的门限,因此它适合于具有突发特性的流量。(这部分是网上很多博客都错误的地方)
例子演示:
首先我们配置了limt_req_zone,rate=10r/m,即每六秒才处理一次请求,如下:
1.首先测试不加burst和不加nodelay的情况:
2.只加burst和不加nodelay的情况:
使用ab测试工具,发起10个并发请求:
上图是ab测试第一秒时的截图,TIME_WAIT=5 表示有服务器端主动断开了5个TCP连接,即5个请求被瞬时拒绝,同时ESTABLISHED的数量由23增加到28,即建立了5个TCP连接
https://wenda.zuncuang.com/image/show/attachments-2021-08-qcj3E39I61139934e0d58,pnghttps://wenda.zuncuang.com/image/show/attachments-2021-08-qcj3E39I61139934e0d58,png
可以观察到在39分35秒,即压测第1秒时,成功处理了1个请求,另外有4个请求瞬间返回了503,剩下的5个请求每隔6s处理一次。
这是因为设置了burst=5,在服务器接收到10个并发请求后,先处理1个请求,同时将5个请求放入burst缓冲队列中,等待处理。而超过(burst+1)数量的请求就被直接抛弃了,即直接抛弃了4个请求。
查看 /var/log/nginx/error.log日志
3.加burst和加nodelay的情况:
接着查看 /var/log/nginx/access.log日志
可以发现在1s内,服务器端处理了6个请求(峰值速度:burst+原来的处理速度)。对于剩下的4个请求,直接返回503,在下一秒如果继续向服务端发送10个请求,服务端会直接拒绝这10个请求并返回503。因为设定了没6s处理1个请求,所以直到30 s 之后,才可以再处理一个请求,即如果此时向服务端发送10个请求,会返回9个503,一个200
查看/var/log/nginx/error.log日志,发现有4个请求被直接拒绝了,没有延时请求。
总结:
limit_req zone=req_zone;
严格依照在limti_req_zone中配置的rate来处理请求
超过rate处理能力范围的,直接drop
表现为对收到的请求无延时
limit_req zone=req_zone burst=5;
依照在limti_req_zone中配置的rate来处理请求
同时设置了一个大小为5的缓冲队列,在缓冲队列中的请求会等待慢慢处理
超过了burst缓冲队列长度和rate处理能力的请求被直接丢弃
表现为对收到的请求有延时
limit_req zone=req_zone burst=5 nodelay;
依照在limti_req_zone中配置的rate来处理请求
同时设置了一个大小为5的缓冲队列,当请求到来时,会爆发出一个峰值处理能力,对于峰值处理数量之外的请求,直接丢弃
在完成峰值请求之后,缓冲队列不能再放入请求。如果rate=10r/m,且这段时间内没有请求再到来,则每6 s 缓冲队列就能回复一个缓冲请求的能力,直到回复到能缓冲5个请求位置。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!