如何在Nginx中基于并发连接数动态调整速率限制?
如何在Nginx中基于并发连接数动态调整速率限制?
嘿,这个需求挺实用的——根据服务器并发连接数动态调整速率限制,避免高并发时服务被冲垮。我来给你一步步拆解实现方案,分两种场景(按IP统计并发/全局并发),还有不同Nginx版本的兼容方案。
核心思路
要实现这个需求,我们需要结合Nginx的三个核心模块:
ngx_http_limit_conn_module:用来统计并发连接数(单IP或全局)ngx_http_map_module:根据并发数的范围映射对应的速率限制值ngx_http_limit_req_module:应用动态计算出的速率限制
方案1:按单个IP的并发连接数调整速率(Nginx 1.17.6+)
如果你的Nginx版本在1.17.6及以上,支持limit_req_zone的rate参数使用变量,配置会更简洁:
http { # 1. 统计每个IP的并发连接数,创建一个10M的存储区域 limit_conn_zone $binary_remote_addr zone=ip_conn:10m; # 2. 根据并发数映射对应的速率限制 # 区间匹配需要Nginx 1.13.10+,低版本可以用正则替代 map $limit_conn_ip_conn $dynamic_rate { 0-19 10r/m; # 0-19并发 → 10请求/分钟 20-29 9r/m; # 20-29并发 → 9请求/分钟 30-39 8r/m; 40-49 7r/m; 50-59 6r/m; 60-69 5r/m; 70-79 4r/m; 80-89 3r/m; 90-100 2r/m; # 90-100并发 → 最低2请求/分钟 default 2r/m; # 超过100并发也保持最低值 } # 3. 定义速率限制区域,使用动态计算的速率 limit_req_zone $binary_remote_addr zone=dynamic_limit:10m rate=$dynamic_rate; # 你的上游服务器配置 upstream myservers { server 127.0.0.1:8081; server 127.0.0.1:8082; server 127.0.0.1:8083; server 127.0.0.1:8084; } server { listen 80; # 主监听端口,可根据你的需求修改 server_name your-domain.com; # 限制单个IP的最大并发连接数为100 limit_conn ip_conn 100; # 应用动态速率限制,burst=5允许5个突发请求,nodelay不排队直接处理/拒绝 limit_req zone=dynamic_limit burst=5 nodelay; # 自定义429速率限制超出错误页 error_page 429 /rate-limit-exceeded.html; location = /rate-limit-exceeded.html { root /path/to/your/error-files; # 替换成你的错误页目录 internal; # 只允许内部跳转访问 } # 转发请求到上游服务器 location / { proxy_pass http://myservers; # 补充你的proxy配置(比如proxy_set_header等) proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } # 你的后端服务器配置(原来的8081等端口) server { listen 8081; # 后端服务的具体配置... } # 其他后端server块同理... } }
方案2:按全局服务器的并发连接数调整速率
如果你的需求是基于整个服务器的总并发数(而不是单个IP),只需要修改limit_conn_zone的统计变量:
# 替换原来的limit_conn_zone,用$server_name统计全局并发 limit_conn_zone $server_name zone=global_conn:10m; # map部分改用全局并发数变量 map $limit_conn_global_conn $dynamic_rate { 0-19 10r/m; 20-29 9r/m; # ... 其他区间和之前一致 90-100 2r/m; default 2r/m; }
兼容低版本Nginx(<1.17.6)
如果你的Nginx版本低于1.17.6,不支持rate参数用变量,那就需要创建多个速率限制区域,再通过map选择对应的区域:
http { limit_conn_zone $binary_remote_addr zone=ip_conn:10m; # 预先创建所有需要的速率限制区域 limit_req_zone $binary_remote_addr zone=rate10:10m rate=10r/m; limit_req_zone $binary_remote_addr zone=rate9:10m rate=9r/m; limit_req_zone $binary_remote_addr zone=rate8:10m rate=8r/m; limit_req_zone $binary_remote_addr zone=rate7:10m rate=7r/m; limit_req_zone $binary_remote_addr zone=rate6:10m rate=6r/m; limit_req_zone $binary_remote_addr zone=rate5:10m rate=5r/m; limit_req_zone $binary_remote_addr zone=rate4:10m rate=4r/m; limit_req_zone $binary_remote_addr zone=rate3:10m rate=3r/m; limit_req_zone $binary_remote_addr zone=rate2:10m rate=2r/m; # map选择对应的速率区域 map $limit_conn_ip_conn $rate_zone { 0-19 rate10; 20-29 rate9; 30-39 rate8; 40-49 rate7; 50-59 rate6; 60-69 rate5; 70-79 rate4; 80-89 rate3; 90-100 rate2; default rate2; } upstream myservers { # 上游配置不变... } server { listen 80; server_name your-domain.com; limit_conn ip_conn 100; # 使用选择好的速率区域 limit_req zone=$rate_zone burst=5 nodelay; # 错误页配置不变... location / { proxy_pass http://myservers; } } }
关键注意事项
- 版本要求:区间匹配(
0-19这种写法)需要Nginx 1.13.10+;rate用变量需要1.17.6+,低版本请用正则或多区域方案。 - 并发数统计:要明确是统计单IP还是全局并发,选择对应的
limit_conn_zone变量。 - 错误处理:除了429速率限制错误,当并发超过100时会返回503错误,你可以添加
error_page 503 /maintenance.html;来处理。 - 存储区域大小:
zone=xxx:10m的10M是存储区域大小,根据你的并发量调整,一般1M可以存储约16000个IP的信息。
备注:内容来源于stack exchange,提问作者Ilgar




