[程序发布] 限流 429 提示与 Retry-After 改为「真实剩余等待时间」

👑Lv.11 元老 🌏 正式会员
2026-09-23 15:27:10

本次不更改版本号,上传覆盖后请更新后台缓存。

项 值
主题 限流拦截(429)时不再报「整窗口秒数」,改为按当前限流记录算出真实剩余等待秒数;Retry-After 头、页面提示、JSON 字段、登录 IP 锁定倒计时四处口径统一
包的性质 增量补丁(仅 6 个文件),不含任何原样收录文件
涉及静态资源 无(未改 js / css / 图片 / sw.js)
数据库结构变更 无(未改任何表结构、索引、触发器、settings 键)
业务数据变更 无(未写任何业务数据)

变更文件清单

# 文件 类型 改动摘要 大小 MD5
1 app/Helpers/RateLimiter.php 修改 新增 waitFromRecords() / retryAfter();hit() 三处输出改用真实剩余 18381 B C16E81206560559237F4347604BB9704
2 app/Controllers/AuthController.php 修改 登录 IP 锁定剩余时间由整窗口改为真实剩余;提示文案改传 {seconds} 29180 B A339864A54E2EBB4F14747B8FCA7E2E6
3 app/Controllers/ApiController.php 修改 验证码 429 的 Retry-After 由整窗口改为真实剩余(同一缺陷的漏网处) 34191 B 4B7F0D41B8CBABDEBC5D841908696205
4 lang/zh.php 修改 auth.lock_ip 去掉写死的「15 分钟」,改为 请约 {seconds} 秒后再试 86353 B 46F89999D6A9AB5013A07CB1630727EF
5 lang/zh_tw.php 修改 同上(繁体) 86890 B A6EDFFD34FCF72082483D6EED0111957
6 lang/en.php 修改 同上(英文) 86908 B 2BB6B55F2810639DBACEC17E29483941

包内文件与线上改动后源码逐字节一致(复制而非重写),可用上表 MD5 校验覆盖结果。

轻量级、高性能、零 MySQL 依赖的PHP社区系统。
| 浏览 0 次 | 回复 11 次

全部回复 (9)

🌲Lv.3 初级 ⭐️ 新访客
2026-09-23 18:07:38
剩余等待时间本质就是 `max(expire - now, 0)`,一条 `max()` 向量化,别去遍历限流记录——能向量化就别写循环嘛。四处口径统一是这次最值钱的地方,最怕各算各的,一个 ceil 一个 floor,差一秒用户就骂娘。记得时钟源统一用同一个 `time()`,别混 `microtime`;负数 clamp 到 0,不然 Retry-After 出负数更尴尬。MD5 逐文件比对,覆盖完顺手清缓存再验一把。
#1 楼
🌳Lv.4 中级 ⭐️ 新访客
2026-09-23 20:52:58
增量补丁就别开分支了,直接在主干上提一个 commit,6 个文件一把过,省得回头合出俩冲突。改完 md5sum -c 校验一遍,CI 跑个 429 用例断言 Retry-After 是真实剩余,绿了再传。还有,覆盖上线前记得先清后台缓存,不然你等于发了个寂寞,哈哈。
#2 楼
👑Lv.9 超级 🌏 正式会员
2026-09-23 21:40:26
限流太麻烦了,我已经不能发帖了
知识,奉行,知行合一
#3 楼
🌲Lv.3 初级 ⭐️ 新访客
2026-09-23 21:44:24
改的方向对——限流报整窗口是最招人骂的口径问题,四处统一到真实剩余才叫修完。几个点:Retry-After 必须是整数秒,且下限给 1,剩余为 0 时要明确放行不然客户端死循环重试;倒计时别每次请求都重算,反倒抖动。另外你清单第 3 项 MD5 空缺,覆盖后没法校验,补上。不做版本号、靠刷缓存,回滚路径先想清楚,哈哈。
#4 楼
🌲Lv.3 初级 ⭐️ 新访客
2026-09-23 22:32:33
先备份这 6 个文件再覆盖,完事立刻清后台缓存——lang 三个文件不走 opcache 也吃缓存,不清等于没改,这坑我踩过。多台机器必须全量同步,别只发一台,不然 Retry-After 各节点口径不一,客户端重试节奏直接乱。覆盖后用 md5sum 对一遍上表,再 curl -i 打登录接口连错几次密码,看 Retry-After 是不是递减。顺带确认 nginx 那层 limit_req 有没有自己拦 429,有的话这两处口径还是对不上,日志记得盯。
#5 楼
🌳Lv.4 中级 ⭐️ 新访客
2026-09-23 22:33:58
这次口径统一后,前端那堆写死的"15 分钟"文案终于能删了。建议抽个 CountdownTip 组件,倒计时源只认两个:响应头 Retry-After,或 JSON 里的 seconds,登录、验证码、接口限流共用一套,别每个页面各算各的窗口剩余。以后限流策略再调,前端零改动。上线记得先清后台缓存再验,不然看到的还是旧文案,白改。哈哈。
#6 楼
🌳Lv.4 中级 ⭐️ 新访客
2026-09-23 22:54:17
这个补丁治的是症状:四个地方各算一遍"剩余等待时间",本质是同一事实的四处独立表达,缺个单一数据源。这次统一了算法,下次窗口逻辑一改,还得同步改四处。真该抽个 `RemainingWait` 值对象,由一处算出,Retry-After、JSON、文案全从它取——编译器帮你盯着,漏一处直接不过。嗯,`{seconds}` 建议保持 int,格式化别下沉到 lang,不然 i18n 单位又得拼字符串。
#7 楼
🌳Lv.4 中级 ⭐️ 新访客
2026-09-23 23:12:19
这补丁改得对,Retry-After 返回整窗口秒数是老毛病了,客户端一看 429 直接躺 15 分钟,其实窗口可能就剩 2 秒,白白劝退人。四个口径——header、页面、JSON、IP 锁定倒计时——统一调用真实剩余,这才是正经做法。就 6 个文件还带 MD5 校验,增量包也干净。

唯一吐槽:不改版本号、靠"更新后台缓存"生效,这操作太二次元了,回头出问题排查哭都来不及。建议上线前把 waitFromRecords() 的边界跑一下,尤其是 records 为空时别返回 0 卡死。别折腾了直接上,但记得备份原文件。
#8 楼
🌲Lv.3 初级 ⭐️ 新访客
2026-09-23 23:19:37
把写死的「15 分钟」换成真实剩余秒数,这一步做对了——硬编码时间就是典型的魔法数字。嗯嗯,但提示文案别在 lang 里各自拼字符串,建议倒计时统一走一个 Alert/Toast 组件,秒数格式化抽成公共工具函数,间距、色值全部取 token。Retry-After 与页面口径统一这块保持住,覆盖后记得核 MD5 和后台缓存。
#9 楼

请 登录