Nginx下limit_req模块burst参数超详细解析

在学习Nginx的时候遇到了这个问题,百度到了很多博客,大多都讲得不清不楚,在看到了

http://www.wangjingfeng.com/730.html

http://cjhust.blog.163.com/blog/static/17582715720111017114121678/

这两篇博客之后终于疑窦瞬开,在综合了之前看到的博客再加上测试案例之后整理成文。

引言:

漏斗算法(Leaky Bucket),该算法有两种处理方式Traffic Shaping和Traffic Policing

在桶满水之后,常见的两种处理方式为:

1.暂时拦截住上方水的向下流动,等待桶中的一部分水漏走后,再放行上方水。
2.溢出的上方水直接抛弃。
 

将水看作网络通信中数据包的抽象,则方式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中该模块的使用配置示例

limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;
 
server {
 
    location  /search/ {
 
        limit_req zone=one burst=5 nodelay;
 
    }


第一段配置参数:

  • $binary_remote_addr :表示通过remote_addr这个标识来做限制,“binary_”的目的是缩写内存占用量,是限制同一客户端ip地址
  • zone=one:10m:表示生成一个大小为10M,名字为one的内存区域,用来存储访问的频次信息
  • rate=1r/s:表示允许相同标识的客户端的访问频次,这里限制的是每秒1次,即每秒只处理一个请求,还可以有比如30r/m的,即限制每2秒访问一次,即每2秒才处理一个请求。
     

第二段配置参数:

  • zone=one :设置使用哪个配置区域来做限制,与上面limit_req_zone 里的name对应
  • burst=5:重点说明一下这个配置,burst爆发的意思,这个配置的意思是设置一个大小为5的缓冲区当有大量请求(爆发)过来时,超过了访问频次限制的请求可以先放到这个缓冲区内等待,但是这个等待区里的位置只有5个,超过的请求会直接报503的错误然后返回。
  • nodelay:
  • 如果设置,会在瞬时提供处理(burst + rate)个请求的能力,请求超过(burst + rate)的时候就会直接返回503,永远不存在请求需要等待的情况。(这里的rate的单位是:r/s)
  • 如果没有设置,则所有请求会依次等待排队
     

     这里的burst参数主要采用了令牌桶算法。令牌桶算法是网络流量整形(Traffic Shaping)和速率限制(Rate Limiting)中最常用的一种算法。典型情况下,令牌桶算法用来控制发送到网络上的数据数目,并允许突发数据的发送。

     令牌桶这种控制机制基于令牌桶中是否存在令牌来指示什么时候可以发送流量。令牌桶中的每一个令牌都代表一个字节。如果令牌桶中存在令牌,则允许发送流量;如果令牌桶中不存在令牌,则不允许发送流量。因此,如果突发门限被合理地配置并且令牌桶中有足够的令牌,那么流量就可以以峰值速率发送。令牌桶算法的基本过程如下:

 

1.假如用户配置的平均发送速率为10r/s,则每隔0.1秒一个令牌被加入到桶中;

 

2.假设桶最多可以存发b个令牌。如果令牌到达时令牌桶已经满了,那么这个令牌会被丢弃;

 

3.当一个n个字节的数据包到达时,就从令牌桶中删除n个令牌,并且数据包被发送到网络;

 
4.如果令牌桶中少于n个令牌,那么不会删除令牌,并且认为这个数据包在流量限制之外;
 
5.算法允许最长b个字节的突发,但从长期运行结果看,数据包的速率被限制成常量r。对于在流量限制外的数据包可以以不同的方式处。   理:
        1.它们可以被丢弃;
        2.它们可以排放在队列中以便当令牌桶中累积了足够多的令牌时再传输;
        3.它们可以继续发送,但需要做特殊标记,网络过载的时候将这些特殊标记的包丢弃。
 

注意:令牌桶算法不能与另外一种常见算法“漏斗算法(Leaky Bucket)”相混淆。这两种算法的主要区别在于“漏斗算法”能够强行限制数据的传输速率,而“令牌桶算法”在能够限制数据的平均传输数据外,还允许某种程度的突发传输。在“令牌桶算法”中,只要令牌桶中存在令牌,那么就允许突发地传输数据直到达到用户配置的门限,因此它适合于具有突发特性的流量。(这部分是网上很多博客都错误的地方)

 

例子演示:

首先我们配置了limt_req_zone,rate=10r/m,即每六秒才处理一次请求,如下:

1.首先测试不加burst不加nodelay的情况:

  • 查看当前的tcp连接数
  • netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
  • 结果如下:

                   

  • 使用ab测试工具,发起10个并发请求:
  • ab -n 10 -c 10 url
  • ab压测工具瞬间返回了结果
  • 可以看到一共10个请求,9个请求都失败了。且0.09秒就完成了压测

接着查看当前的tcp连接数:

可以观察到此时服务端的TIME_WAIT 为10,这意味着是服务端主动要求断开了所有TCP连接

  • 接着再查看 /var/log/nginx/access.log,印证了只有一个请求成功了,其它就是都直接返回了503,即服务器拒绝了请求。

2.只加burst和不加nodelay的情况:

  • 查看当前的tcp连接数
  • netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
  • 结果如下:

                   

  • 使用ab测试工具,发起10个并发请求:

                   ab -n 10 -c 10 url
可以看到测试经过30 s 才结束

  • 压测中一共10个请求,有4个请求失败了,直接返回了503

 

  • 查看当前的tcp连接数

          

  • 上图是ab测试第一秒时的截图,TIME_WAIT=5 表示有服务器端主动断开了5个TCP连接,即5个请求被瞬时拒绝,同时ESTABLISHED的数量由23增加到28,即建立了5个TCP连接
  • 上图是ab测试过程中的截图,TIME_WAIT=7 表示有服务器端主动断开了7个TCP连接,增加的2个TIME_WAIT是因为有2个在缓存队列的请求被处理完毕了,所以断开了连接。

 
接着查看 /var/log/nginx/access.log日志

  • 可以观察到在39分35秒,即压测第1秒时,成功处理了1个请求,另外有4个请求瞬间返回了503,剩下的5个请求每隔6s处理一次。
  • 这是因为设置了burst=5,在服务器接收到10个并发请求后,先处理1个请求,同时将5个请求放入burst缓冲队列中,等待处理。而超过(burst+1)数量的请求就被直接抛弃了,即直接抛弃了4个请求。

查看 /var/log/nginx/error.log日志


发现有5个delaying request,4个limiting request

3.加burst和加nodelay的情况:

  • 查看当前的tcp连接数
  • netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
  • 结果如下:

                   

  • 使用ab测试工具,发起10个并发请求:

           ab -n 10 -c 10 url

  • 可以看到压测在0.1 s 内完成了,这也是添加nodelay参数的意义
  • 压测中一共10个请求,有4个请求失败了,直接返回了503
  • 查看当前的tcp连接数:所有的请求都在1s内处理完成了
  • 接着查看 /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个请求位置。

 

 


原文链接:https://blog.csdn.net/hellow__world/article/details/78658041

<think>我们正在处理一个关于Nginx配置错误的问题。用户报告的错误是“directive limit_req_zone is not terminated by semicolon”,这意味着在配置文件中,`limit_req_zone`指令没有以分号正确结束。 根据引用[1]和[2],我们知道`limit_req_zone`指令用于限制客户端的请求频率。它的基本语法结构如下: limit_req_zone key zone=name:size rate=rate; 常见的错误是在定义区域时忘记在行末添加分号,或者参数之间缺少必要的分隔符(如空格或分号)。 因此,我们需要检查配置文件中`limit_req_zone`指令的每一部分,确保: 1. 指令的每个参数之间用空格分隔。 2. 整个指令以分号(;)结束。 例如,一个正确的配置可能是: limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s; 注意:在`zone=one:10m`和`rate=1r/s`之间有一个空格,最后以分号结束。 另外,引用[3]和[4]提供了关于`limit_conn`模块的配置说明,虽然与`limit_req`模块不同,但配置指令的语法规则类似,都需要以分号结束。 因此,解决这个错误的方法是检查Nginx配置文件中使用`limit_req_zone`的语句,确保它正确结束(即最后有一个分号)。同时,检查指令中的各个参数是否正确(如key、zone名称、内存大小、速率等)。 如果用户提供了具体的配置行,我们可以帮助检查。但根据错误信息,最可能的原因就是缺少分号。 下面,我们按照用户要求的结构化方式来回答。</think>### Nginx 配置错误:`limit_req_zone` 指令未正确终止 **错误原因分析** 当出现 `directive "limit_req_zone" is not terminated by ";"` 错误时,根本原因是 `limit_req_zone` 指令的语法格式不正确,主要涉及两点: 1. **缺少结束分号**:指令末尾未用分号 `;` 终止 2. **参数分隔错误**:指令参数之间缺少必要的空格分隔符[^1][^2] --- ### 正确配置格式(对比示例) #### ❌ 错误写法(触发报错) ```nginx limit_req_zone $binary_remote_addr zone=rate_limit:10m rate=10r/s # 缺少分号 limit_req_zone$binary_remote_addr zone=rate_limit:10m rate=10r/s; # 参数间缺少空格 ``` #### ✅ 正确写法 ```nginx # 语法:limit_req_zone KEY zone=NAME:SIZE rate=RATE; limit_req_zone $binary_remote_addr zone=rate_limit:10m rate=10r/s; # 参数有空格 + 分号终止 ``` --- ### 关键参数说明 | 参数 | 作用 | 示例值 | |-----------------------|----------------------------------------------------------------------|----------------------| | `$binary_remote_addr` | 基于客户端IP限流(二进制格式更省内存) | 必填 | | `zone=name:size` | 定义共享内存区(`name`为区域名,`size`决定存储容量) | `zone=my_zone:10m` | | `rate=...` | 请求速率限制(`r/s`=每秒请求数,`r/m`=每分钟) | `rate=5r/s` | | `;` | **必须的终止符** | 指令末尾 | --- ### 完整配置示例 ```nginx http { # 步骤1:在http块定义限流规则 limit_req_zone $binary_remote_addr zone=api_limit:20m rate=100r/m; # ✅ 正确终止 server { listen 80; # 步骤2:在location应用限流 location /api/ { limit_req zone=api_limit burst=50 nodelay; # 突发请求处理 proxy_pass http://backend; } } } ``` --- ### 调试建议 1. **检查分号**:确保每个 `limit_req_zone` 指令末尾有 `;` 2. **验证空格**:确认参数间有空格分隔(特别是 `zone=` 和 `rate=` 前) 3. **测试配置**:运行 `nginx -t` 验证语法 ```bash nginx -t # 输出应显示 "syntax is ok" ``` 4. **内存容量**:确保 `zone` 的 `size` 值足够(如 `10m` 可存储约16万IP状态)[^3] > ⚠️ **注意**:此指令仅能在 `http` 上下文中使用,不可放入 `server` 或 `location` 块[^1][^3]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值