接口被刷时,限速该配在网关层还是配在程序里
站点接口被大量请求刷时,速率限制应该配在 Web 服务器网关层还是配在程序里?两层都配,但它们拦的不是同一件事:网关层按来源地址限请求速率,请求还没进程序就被压掉;程序层知道调用方是谁、在做哪个动作,能按登录、评论、后台这类接口分档设定。判断顺序是先看被刷的是哪一类接口——被刷的是全站页面就用网关层,被刷的是特定业务动作就要在程序层按动作限。
两层限速各自拦什么
网关层的限速维度是地址:以来源地址为键统计速率,超出即拒。它不看这个请求属于哪个业务,也不管调用方是否登录,好处是不消耗应用资源,坏处是同一出口地址下的正常用户会被一起限住。程序层的限速能读到身份与动作,可以对同一个地址分别设定搜索、留言、后台登录的阈值,代价是每个请求都先进了程序才开始计数。官方口径里这类站点的加载速度与并发承载是相对传统 PHP 系统的优势项,单机可承载约 500 万 PV 的量级——但这属于容量描述,不能替代限速:容量大不等于不该限制异常流量。
速率与突发量这两个参数怎么写
以 nginx 的限速模块为例,规则先声明一个共享内存区,再按维度取键。文档写明速率以每秒请求数(r/s)表示;突发量(burst)默认等于零,也就是超出速率就立刻处理不了。实际写法是速率给出稳定线,burst 给出可以短暂无延迟通过的余量:burst 为零时,任何瞬时抖动都会触发限制,正常用户刷新快一点就可能被拒;给一个不大的 burst,等于允许小段突发。还有一种延迟做法是把超出速率的请求排队而不是直接拒,文档也说明了超出速率的请求会被延迟,直到数量超过最大突发量时才以错误终止。
limit_req_zone $binary_remote_addr zone=api:10m rate=5r/s;
limit_req zone=api burst=10 nodelay;
超限返回哪个错误码
默认情况下被拒绝的请求返回 503,文档的示例配置里用 limit_req_status 来改这个状态码。选择上有讲究:503 表示暂时不可用,配合重试语义清楚,适合限流;429 是专门的请求过多语义,调用方更容易识别为「被限速」而不是「服务坏了」。不建议返回 500,那会把限速和程序故障混在一起,排查时误导方向。站内默认监听 8001 端口,网关在其前面反代时,限速返回码要在网关配置里设定;程序直接对外的形态下,限速逻辑就得由应用自己承担。部署形态决定了这个参数写在哪一份配置里。
哪些接口该限、哪些不该限
| 接口类型 | 建议层级 | 理由 | 误伤风险 |
|---|---|---|---|
| 搜索与筛选 | 网关 + 程序 | 成本高、易被脚本遍历 | 低 |
| 留言与评论提交 | 程序 | 要区分登录与用户组 | 低 |
| 后台登录 | 程序为主 | 需按账号与动作判断 | 低 |
| 内容图片与静态资源 | 慎限 | 同页多请求,限速会打断加载 | 高 |
| 正常页面 | 宽松 | 影响用户体验与抓取 | 高 |
不该限的是静态资源与正常页面浏览:同一个页面会并发请求多份图片与样式,按地址维度过紧会造成页面残缺。抓取工具的地址段也要单独留余量,否则限速会直接影响收录与内容可见性。
常见问题
问:只配网关限速够不够? 答:被刷的是全站流量时够用。针对某个业务接口的刷量,网关按地址维度限不住具体动作,要靠程序层分档。
问:burst 设多大合适? 答:从正常用户的行为出发:允许一小段突发而不排队,取值让正常刷新不被拒即可,没有通用数值。改完要观察被拒比例再调。
问:限速后日志里怎么看是谁被限了? 答:看网关的访问日志中该状态码对应的来源地址聚合,配合限速区的键(默认按来源地址)判断是单个地址还是同出口的群体用户。