一 概述
ngx_http_limit_req_module
模块可以用来限制请求频率。我们要明确限制的请求对象,如何区分一个请求?可以使用请求对端地址、URL 参数或者其他头\包体信息。NGINX 使用多进程模式,如果仅在进程内实现频率限制,同一个请求被均衡到其他进程时频率限制会失效,此时需要使用共享内存同步频率信息。
二 模块说明
ngx_http_limit_req_module
介入阶段:NGX_HTTP_PREACCESS_PHASE
。
1. limit_req_zone
指令
1 | # 指令 |
可使用域:http
功能:设置一块共享内存用来保存 key
的状态参数。如果限制域的存储空间耗尽了,对于后续所有请求,服务器都会返回 503。
字段 | 含义 | 可取值 |
---|---|---|
key | 使用 key 在共享内存中保存/查询其对应值 | 变量(例如 $binary_remote_addr)、字符串(name)或者两者混合 |
rate | 每个 key 平均访问频率限制 | 1r/s(每秒一个请求),10r/m(每分钟十个请求) |
zone | 共享内存声明,name 为共享内存名,size 为共享内存大小 |
2. limit_req
指令
1 | # 语法 |
可使用域:http\server\location
字段 | 含义 | 可取值 |
---|---|---|
zone | 引用 limit_req_zone 定义的共享内存 |
limit_req_zone 定义的 name |
burst | 突发请求数,默认值为零,设置允许超过频率限制的请求数。如果超过平均频率的请求数小于 burst,那么请求会被延时处理;如果超过平均频率的请求数大于 burst,那么会返回 503 | 数值 |
nodelay | 如果未设置 nodelay ,在超过频率后会将当前请求放入定时器中,待时间到达后再进行请求处理。如果设置了 nodelay 则请求处理结束 |
nodelay |
3. limit_req_status
1 | # 语法 |
可使用域:http\server\location
功能:设置拒绝请求的响应状态码
4. limit_req_log_level
1 | # 语法 |
可使用域:http\server\location
功能:设置因为访问频率过高拒绝或延迟处理请求时记录的日志级别,limit_req_log_level
设置的是拒绝处理请求的日志级别,延迟处理请求的日志级别比拒绝处理请求的日志级别低一个级别。例如:limit_req_log_level notice
拒绝处理的日志级别为 notice
,延迟的日志级别为 info
。
三 实现
在 ngx_http_limit_req_module
中 key
使用红黑树结构存储。
1. 模块声明与指令定义
1 | // 指令定义 |
2. 共享内存初始化
1 | static char * |
ngx_http_limit_req_zone
函数并没有立即创建共享内存,仅将共享内存配置信息解析并放在 cycle
中统一管理。配置解析,功能实现分开,思路非常棒,凡事都理清。
在调用 ngx_http_limit_req_init_zone
之前 NGINX 已经根据配置创建好了共享内存,在 ngx_http_limit_req_init_zone
函数中主要做的是如何使用这块共享内存。NGINX 已经将共享内存申请的重复工作封装好,我们只需要将共享内存配置信息创建好,它可以替我们创建好共享内存直接使用。
1 | static ngx_int_t |
3. 访问限制配置
limit_req_zone
指令用来声明共享内存,limit_req
指令用来在当前范围启用访问限制。limit_req
指令主要功能是将访问限制配置添加到main\server\location
级别的模块配置结构体中,main\server\location
级别可以有多个访问限制,通过动态数组来组织。
1 | static char * |
4. 共享内存查找
ngx_http_limit_req_lookup
用于在共享内存中查找、新增节点。当未查找到节点时会分配新的节点用以存储当前请求统计信息,并将当前节点加入 LRU 队列首部;当查找到时会将 LRU 队列中当前节点重新插入到首部,并计算请求频率以及超出值更新到节点中,根据是否超出返回相应应答。account
参数用来判断当前访问现在是否是当前域的最后限制,**limit_req
模块仅在最后一个访问限制配置中保存访问信息**。
1 | static ngx_int_t |
5. 业务处理接口
ngx_http_limit_req_handler
函数介入 NGX_HTTP_PREACCESS_PHASE
处理阶段。
1 | static ngx_int_t |
6. 延时后处理
如果请求需要延时处理会将请求放入定时器中,当定时时间到后会执行定时器中超时处理函数 ngx_http_limit_req_delay
。
1 | static void |
四 HTTP 应答码解释
204
服务器成功处理了请求,没有返回任何内容。
444
Nginx 上 HTTP 服务器扩展。服务器不向客户端返回任何信息,并关闭连接(有助于阻止恶意软件)。
503
服务不可用。
五 总结
在使用 ngx_http_limit_req_module
时可以自己添加变量,变量值为请求头或包体中内容用于区分请求,直接使用官方模块实现访问频率限制。
六 ngx_http_limit_conn_module
说明
ngx_http_limit_conn_module
与本模块功能相似,但是 limit_conn
的限制对象是连接数,只需要将键的连接数保存在共享内存中就可以。不过 limit_conn
同样有另外一个问题,在连接建立请求时可以累计连接数,但是连接关闭时如何减少连接数呢?limit_conn
采用的方法是在当前请求的连接池清理函数中添加清理函数 ngx_http_limit_conn_cleanup
用于减少计数清理内存。
ngx_http_limit_conn|req_module
模块在共享内存空间不够时均返回配置的应答码给客户端。