3 月一场展会,扫码报名,开门那半小时最挤。上线前我们没申请加机器,把能挡的先挡在前面。
三层
第一层在 Nginx。按 IP 限流,每个 IP 一分钟 20 次请求,超了直接 429。展会现场一个 Wi-Fi 出口,几十台手机共享同一个公网 IP,这个阈值调过两轮:先设 10,误伤了一批正常用户,改到 20 才顺。用 limit_req_zone 配,burst 给了 5。
第二层在 Redis。扫码进来的页面要读报名状态,原来每次查库。改成 Redis 存一份,300 秒过期;同一个二维码 10 秒内重复点,直接返回上一次的结果,不往下走。现场网络抖,手指容易连点两下。
第三层在写库前。按二维码 token 加手机号后四位做幂等键,60 秒内重复提交只写一条,重复的返回已有记录。这一层是兜底,前两层没拦住的到这儿也停下了。
静态和提交分开
页面本身、二维码图片、样式全推 CDN,文件名带内容哈希,缓存 30 天。表单提交走 submit 独立域名,CDN 明确不缓存、不加速,直接回源,跟 2020 年活动站点那次一个套路。第一版图省事同域,POST 被边缘节点拦过 403。
现场
3 月 6 号,9 点开门。峰值一分钟 600 多次提交,Redis 的 CPU 一直很平,MySQL 写入 QPS 最高 80 多,因为大部分重复在前面就被挡了。
被 429 拦掉的 200 多次,翻日志看多数是同一个扫码枪在连发,还有几个是有人反复刷新结果页。没影响正常报名。
3 小时里没有 502,队列最长堆到 30 多条,两分钟排空。
一点后话
限流这东西,阈值不是算出来的,是拿真实网络试出来的。同一个出口 IP 下面到底多少人,现场才知道。所以那天我们留了一个可以随时改的配置,不改代码、不重启,改完 reload 就生效。真调了两次。
散场导了一份 CSV,2000 多行,按扫码来源分了 3 个 sheet。收摊时展馆在撤展板,扫码立牌歪在一边,膜还是没撕干净。

