首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >秒杀接口一开抢就 502:Nginx limit_req 漏桶、真实 IP 与 Redis 库存预热的记录

秒杀接口一开抢就 502:Nginx limit_req 漏桶、真实 IP 与 Redis 库存预热的记录

原创
作者头像
用户9660458
发布于 2026-10-13 09:13:30
发布于 2026-10-13 09:13:30
270
举报

上周五朋友的商城搞周年庆秒杀,十点整开抢,我盯着监控看。前 30 秒一切正常,第十秒开始接口响应从 200ms 飙到 8 秒,第十五秒直接大片 502,后台数据库连接池被打满,正常用户也进不来。朋友在群里连发三条语音:"快关活动!客服已经被骂爆了!"

后来复盘,问题不在数据库本身,而在于秒杀请求是普通商品页的几十倍,Nginx 那层没做任何限流,所有请求直接打到后端 Tomcat,连接池瞬间耗尽。前后调了一下午把限流配上,记录下来。

一、先认清:漏桶和令牌桶的区别

Nginx 自带的 limit_req 是漏桶模型:请求按固定速率流出,突发流量会被排队或直接拒绝。这正好是秒杀场景要的——不管前端来多少请求,后端只按它能扛的速度处理:

代码语言:txt
复制
# http 块定义限流区
limit_req_zone $binary_remote_addr zone=seckill_limit:10m rate=10r/s;
limit_conn_zone $binary_remote_addr zone=seckill_conn:10m;

# location 块引用
location /api/seckill/buy {
    limit_req zone=seckill_limit burst=20 nodelay;
    limit_conn seckill_conn 5;
    limit_req_status 429;
    limit_conn_status 429;
    proxy_pass http://tomcat_backend;
}

几个关键参数:

  • rate=10r/s:每个 IP 每秒放行 10 个请求,超出进桶。
  • burst=20 nodelay:允许突发 20 个排队,但不延时处理,超过 20 直接 429。
  • limit_conn 5:同一个 IP 同时最多 5 个连接,防止一直占着连接不释放。

二、别限 IP:CDN 后面所有人都是同一个 IP

这里踩了个大坑:商城前面挂了 CDN 和负载均衡,Nginx 看到的客户端 IP 全是 CDN 节点的 IP。按 $binary_remote_addr 限流等于给所有真实用户共享一个桶,正常用户也被 429。

必须先拿到真实 IP:

代码语言:txt
复制
# 用真实客户端 IP 作为限流 key
set_real_ip_from CDN网段;
real_ip_header X-Forwarded-For;
real_ip_recursive on;

limit_req_zone $http_x_forwarded_for zone=seckill_limit:10m rate=10r/s;

我们一开始没加这段,压测时 50 个真实用户全被当成同一个 IP,一半请求 429,查了半小时才发现是限流 key 取错了。

三、Nginx 挡不住的,用黑名单和接口前置缓存

限流只能挡"同一个人猛点",挡不住脚本换 IP 批量刷。这种要在应用层配合:

  1. 秒杀接口先查 Redis 库存,库存为 0 直接返回"已抢完",不查数据库。
  2. 同一个用户秒杀接口 5 秒内只放一次(Redis SET NX EX 5),重复点击直接挡。
  3. 风控识别到异常 UA / 请求间隔过短的 IP,拉黑 10 分钟。
代码语言:txt
复制
// 伪代码:秒杀前先做用户级限流
String key = "seckill:lock:" + userId + ":" + skuId;
Boolean ok = redis.opsForValue().setIfAbsent(key, "1", 5, TimeUnit.SECONDS);
if (!Boolean.TRUE.equals(ok)) {
    return Result.fail("操作太频繁,请稍后再试");
}

四、踩坑清单(5 条实际踩过的)

  1. limit_req_zone 要写在 http 块,不能写在 server/location 里,否则 Nginx 重载报错。
  2. 前面有 CDN/网关时,限流 key 必须用真实 IP,别用 $binary_remote_addr,否则误杀一片。
  3. burst 不是越大越好,burst=200 等于把洪峰放进来压后端,按后端实际承受能力设。
  4. 429 状态码要在网关层友好降级,别让用户看到浏览器默认错误页。
  5. 限流只是第一道,库存预热 Redis + 用户维度防重 + 风控黑名单要一起上。

防护层

挡什么

典型配置

Nginx limit_req

单 IP 突发洪峰

rate=10r/s burst=20

Nginx limit_conn

长连接占用

同 IP 5 连接

Redis 库存

后端 DB 被打满

预热库存到内存

用户级 SET NX

同一用户狂点

5 秒窗口

这次周年庆秒杀的限流改造是在乔拓云的商城后台上做的,我主要负责配 Nginx 漏桶规则、修真实 IP 取值和 Redis 库存预热那套逻辑,改完后第二次演练同样的并发量,后端响应稳定在 300ms 以内,502 再没出现过。

复盘

  • 秒杀被打垮往往是 Nginx 一层没限流,所有洪峰直接砸后端连接池。
  • CDN 后面限流 key 一定要取真实 IP,否则会把正常用户误杀。
  • Nginx 限流挡脚本换 IP 的能力有限,必须配合 Redis 库存和用户维度防重。

以上是个人实践记录,各平台具体功能以官方实时信息为准。

你们做秒杀防护时,限流是放在 Nginx 层还是在网关/应用层统一做?

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 一、先认清:漏桶和令牌桶的区别
  • 二、别限 IP:CDN 后面所有人都是同一个 IP
  • 三、Nginx 挡不住的,用黑名单和接口前置缓存
  • 四、踩坑清单(5 条实际踩过的)
  • 复盘
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档