Throttle中间件必须显式绑定路由才生效,需配置Redis缓存、精准设计key(含真实IP与接口路径),并确保Nginx透传及PHP清洗X-Forwarded-For以获取真实IP。

Throttle 中间件不是装完就能用的,它必须显式挂载到路由上才生效;不绑定、不配 Redis、不透传真实 IP,限流就等于没设。
Throttle中间件必须显式绑定到路由才能生效
很多人以为在 app/middleware.php 里注册了 thinkmiddlewareThrottle::class 就万事大吉,其实这只是声明“可用”,真正触发限流靠的是路由层的 ->middleware() 调用。
- 全局启用(不推荐粗粒度):确保
config/app.php中'use_global_middleware' => true开启,再在路由分组中调用:Route::group('api', function () { /* ... */ })->middleware('throttle:60,1'); - 接口级精细控制(推荐):直接在单条路由后绑定,例如:
Route::post('api/upload', 'Api/Upload.index')->middleware(thinkmiddlewareThrottle::class, ['visit_rate' => '5/m', 'key' => '__IP__']); - 常见失效现象:中间件类已注册,但路由定义漏掉
->middleware()—— 此时请求完全绕过限流逻辑,控制器照常执行
Redis 缓存驱动是生产环境的硬性要求
用 file 缓存驱动在并发下会因文件锁导致计数偏差,Throttle 的 INCR 操作在文件驱动里本质是 get + set,高并发时多个请求读到同一旧值,都写入 +1,最终计数远低于实际请求数。
- 必须将
config/cache.php中的default改为'redis',并确认config/redis.php连接参数正确(host/port/auth/database) - 若使用
topthink/think-throttle扩展,其底层依赖Cache::inc(),只有 Redis/Memcached 才保证原子性;文件驱动下该方法退化为非原子操作 - 缓存 key 过期必须可靠:Redis 驱动下
set($key, $val, $ttl)的$ttl若传0可能被忽略,建议显式传正整数秒(如60)
Key 设计决定限流是否精准
key 参数不是可选开关,它直接定义“谁和谁共用一个计数器”。填错等于限错对象,比如所有用户共享一个桶,或同一用户调不同接口被误合并统计。
立即学习“PHP免费学习笔记(深入)”;
-
'key' => '__IP__':按客户端真实 IP 限流,但需先解决代理问题(见下一条) -
'key' => '__CONTROLLER__/__ACTION__':按接口维度限流,适合通用列表接口,但 RESTful 的index方法会被/users和/posts共享计数器 -
'key' => function($throttle, $request) { return $request->ip() . '_' . ($request->param('scene') ?: 'default'); }:上传类接口常用,把业务场景(如头像/封面)纳入 key,避免混用 - 绝对不要写
'key' => true:它等价于只取$_SERVER['REMOTE_ADDR'],在 Nginx 反向代理下必然返回127.0.0.1
真实 IP 获取必须在 Nginx 和 PHP 层协同处理
$request->ip() 默认取 $_SERVER['REMOTE_ADDR'],在 Nginx + PHP-FPM 架构下这个值是内网地址,所有请求算同一个 IP,限流形同虚设。
- Nginx 配置必须透传真实 IP:
proxy_set_header X-Real-IP $remote_addr;和proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - PHP 层需清洗
X-Forwarded-For:该 header 可能是一串逗号分隔的 IP(如"203.204.205.206, 192.168.1.1"),应取第一个公网 IP,过滤掉私有地址段(10.0.0.0/8、127.0.0.0/8等) - 别改框架源码里的
Request::ip()方法——升级时会被覆盖;推荐新建中间件(如CheckIp)提前重写$request的 header 或继承thinkRequest并重写ip() - 闭包路由中
$request->rule()可能为空,fallback 到$request->path()更稳妥
真正容易被忽略的是:限流 key 必须同时包含客户端标识 + 接口路径 + 时间窗口,三者缺一不可;而时间窗口的重置机制是固定窗口(Fixed Window),不是滑动窗口——这意味着“每分钟最多 10 次”指的是从每分钟第 0 秒开始计,而不是过去 60 秒内的累计。
文章来自机圈观察员网,发布者:,转载请注明出处:https://www.jqgcy.com/shoujipingce/124138.html