Nginx中特定API key的每秒请求速率限制不生效问题求助
排查Nginx每秒速率限制不生效的核心原因
- 压测用的API Key与启用的限流规则不匹配
你当前配置里仅启用了key3对应的限流规则limit_req zone=r3 burst=29 nodelay;,key1和key2的规则已经被注释。如果压测时使用的API Key是key1/key2,或者没有携带API-Key请求头,只会触发全局的400r/s限流规则,400并发的请求量刚好符合全局阈值,自然不会出现限流。 - 多限流规则的优先级逻辑问题
Nginx会匹配所有配置的limit_req规则,只要任意一个规则触发就会返回429。你同时配置了单Key的r3限流和全局api_key_header_rate_limit限流,全局阈值远高于单Key阈值,只要单Key规则匹配正常,会先触发单Key限流。如果确实用了key3压测还没触发,优先检查请求头是否正确携带了API-Key: key3,可以在Nginx日志里打印$http_api_key变量确认取值。 - 秒级限流的时间片计算逻辑影响
Nginx的limit_req速率控制是毫秒级粒度:比如30r/s的规则,本质是每33.3ms放行1个请求,搭配burst=29 nodelay的配置,允许一次性突发处理30+29=59个请求,超出部分才会返回429。如果你的JMeter压测时请求不是集中突发,而是分散在1秒以上的时间窗口发送,30r/s的阈值很难触发,你可以调整压测参数把请求集中在100ms内发送,验证限流是否生效。 - Nginx版本兼容性问题
部分老旧的Nginx版本(1.17.1之前)对高频率秒级限流的统计存在精度问题,如果你的Nginx版本过低,可以升级到稳定版后再次验证。
# 已修正原配置中转义字符问题,可直接参考 # Rate Limiter By Input API Key Header limit_req_zone $http_api_key zone=api_key_header_rate_limit:10m rate=400r/s; # API client Rate limiter - START map $http_api_key $r1 { key1 "1"; default ""; } limit_req_zone $r1 zone=r1:10m rate=40r/s; map $http_api_key $r2 { key2 "1"; default ""; } limit_req_zone $r2 zone=r2:10m rate=50r/s; map $http_api_key $r3 { key3 "1"; default ""; } limit_req_zone $r3 zone=r3:20m rate=30r/s; # API client Rate limiter - END server { listen 443 ssl; # listen [::]:443 ssl http2; server_name example.com; root /var/www/html; # Brotli Settings brotli on; brotli_comp_level 5; brotli_buffers 32 8k; brotli_min_length 100; brotli_static on; brotli_types image/jpeg image/bmp image/svg+xml text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript image/x-icon; # Rate Limiter Zone # limit_req zone=r1 burst=39 nodelay; # limit_req zone=r2 burst=49 nodelay; limit_req zone=r3 burst=29 nodelay; limit_req zone=api_key_header_rate_limit burst=399 nodelay; limit_req_status 429; # SSL # listen 443 ssl; ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # managed by Certbot ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # managed by Certbot ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # ssl_protocols TLSv1.2; # ssl_session_timeout 4h; # ssl_handshake_timeout 30s; ssl_client_certificate /etc/nginx/ssl/certificate/api_ca.crt; ssl_verify_client optional; # Set to "on" if you only allow authenticated requests location ~ ^/(status|ping)$ { allow 127.0.0.1; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_index index.php; include fastcgi_params; #fastcgi_pass 127.0.0.1:9000; fastcgi_pass unix:/run/php/php7.4-fpm.sock; } location ^~ /main { alias /var/www/html/main/public; try_files $uri $uri/ @main_laravel; index index.php; location ~ \.php$ { fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_pass unix:/run/php/php7.4-fpm.sock; include fastcgi_params; fastcgi_read_timeout 600; fastcgi_param SCRIPT_FILENAME /var/www/html/main/public/index.php; fastcgi_param SSL_CLIENT_VERIFY $ssl_client_verify; fastcgi_param SSL_CLIENT_S_DN $ssl_client_s_dn; } } location @main_laravel { rewrite /main/(.*)$ /main/index.php?/$1 last; } location / { # index index.html; try_files $uri$args $uri$args/ /index.html; } }
内容的提问来源于stack exchange,提问作者Mahe Krish
相关产品推荐
相关产品推荐

