接口被人刷的时候,第一道防线通常是 Nginx 限流。配置只有几行,但参数理解错了,要么没挡住,要么把自己的正常用户挡了。
基本配置
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
server {
location /api/ {
limit_req zone=api burst=20 nodelay;
limit_req_status 429;
proxy_pass http://backend;
}
}
三个参数分别是
zone=api:10m:共享内存区,10m 大约能存 16 万个 IP 的状态。按 IP 限流用 $binary_remote_addr,比 $remote_addr 省空间(二进制 4 字节 vs 字符串 15 字节)。
rate=10r/s:平均速率,每个 IP 每秒 10 个请求。注意这是平均值,不是硬性每秒计数。
burst=20:允许的突发量。超过速率的请求先排队,队列长度 20。
nodelay:队列里的请求立即处理,不按 rate 排队延迟。不加这个,第 11 个请求会被延迟到下一秒才处理,用户体验是“卡了一下”。加了之后,突发 20 个立刻放行,超过才 429。
常见误配
只写 rate 不写 burst:burst 默认 0,意味着严格按速率放行,稍微有点并发就 503/429。正常用户刷新几次页面就被挡了。
burst 设太大:burst=1000 等于允许瞬间 1000 个请求打到后端,限流形同虚设。burst 应该是“正常用户的峰值”,不是“攻击者的量”。
按 IP 限流但用户在 NAT 后面:一个公司出口 IP 几百人共用,限流会误伤。这种情况下按用户 ID 限流更合适:
limit_req_zone $http_authorization zone=user:10m rate=5r/s;
或者用 map 组合 IP + 路径。
按连接数限流
有的场景限制并发连接比限制速率更合适(比如下载、长连接):
limit_conn_zone $binary_remote_addr zone=conn:10m;
location /download/ {
limit_conn conn 5;
}
白名单
搜索引擎、监控、自己的 IP 不应该被限:
geo $limited {
default 1;
127.0.0.1 0;
192.168.0.0/16 0;
}
map $limited $limit_key {
0 "";
1 $binary_remote_addr;
}
limit_req_zone $limit_key zone=api:10m rate=10r/s;
key 为空字符串时不计入限流,这是官方支持的做法。
观测
限流触发后要看日志确认是真的攻击还是误伤:
awk '$9 == 429 {print $1}' access.log | sort | uniq -c | sort -rn | head
如果某个 IP 被大量 429,去看它的请求路径和时间分布。均匀分布的往往是爬虫,尖峰的是攻击,分散在正常时段的可能是自己的用户被误限。
一句话
限流的目标是“挡住异常,放过正常”。参数要按自己的真实流量来定,抄别人的配置大概率不合适。
非特殊说明,本博所有文章均为博主原创。
如若转载,请注明出处:https://mi-blog.cn/archives/679
共有 0 条评论