thinkphp 实现二级域名独立限流,关键在于将子域名作为维度嵌入统一限流逻辑:一、准确提取子域名并确保 host 头透传;二、redis 键名包含 subdomain 实现隔离;三、按接口级、域名级、用户级分层限频;四、限流中间件前置注册,并用 lua 脚本保障原子性。

ThinkPHP 实现二级域名下 API 独立限流,关键不是给每个子域名配一套限流中间件,而是把子域名作为限流维度嵌入到统一的限流逻辑中——让 admin.example.com、api.example.com、m.example.com 各自拥有独立的计数空间,互不干扰。
一、从 Host 中准确提取子域名
限流前必须能稳定识别当前请求来自哪个子域名。不能依赖 $_SERVER['HTTP_HOST'] 直接截取,要兼顾泛解析、本地开发、反向代理等场景:
- 在中间件或限流类中统一解析:$host = $request->host(); $subDomain = explode('.', $host)[0] ?? 'default';若 host 是 api.example.com,subDomain 就是 api
- 确保 Nginx/Apache 正确透传 Host 头:反向代理时需添加 proxy_set_header Host $host;,避免被覆盖为 127.0.0.1 或上游地址
- TP6 多应用模式下,'url_domain_deploy' => true 必须开启(非 APP_SUB_DOMAIN_DEPLOY),否则 Route::domain() 不生效,子域名识别失败
二、构造带子域名前缀的限流键
Redis 键名必须包含子域名标识,否则所有域名共享同一计数器,起不到隔离作用:
- 推荐格式:rate:api:{$subDomain}:{$ip} 或 rate:api:{$subDomain}:token_{$uid},其中 subDomain 来自上一步解析结果
- 避免裸键如 rate:api:{$ip},它会让 admin.example.com 和 api.example.com 的同一 IP 共享额度
- 若使用 Token 鉴权,优先用用户 ID(如 $request->header('Authorization') 解析出 uid)而非 IP,更精准且防代理穿透
三、分层限频策略(按域名+身份双控)
单靠“每分钟10次”太粗放,需结合业务粒度设计多级防护:
- 接口级限频:例如 rate:api:v1.login:admin:192.168.1.100,限制 admin 子域下单个 IP 调用登录接口的频率
- 域名级兜底:rate:domain:admin:hour 统计 admin.example.com 一小时内总请求数,超 5000 次则临时熔断该域名入口
- 用户级宽松限频:对已登录用户放宽至每小时 100 次,未登录用户严格限制为每分钟 3 次,通过中间件前置判断身份再分流
四、中间件执行时机与原子操作保障
限流必须在控制器执行前拦截,且计数过程不能出现并发超限:
- 将限流中间件注册在 app/middleware.php 全局队列最前端,确保它早于权限、日志等中间件触发
- 禁用 PHP 原生 incr + expire 两步操作,改用 Lua 脚本实现原子递增与首次过期设置:
local current = redis.call("incr", KEYS[1]) if current == 1 then redis.call("expire", KEYS[1], ARGV[1]) end return current - 异常分支也要返回 429 响应:try-catch 中 catch 到 Redis 连接失败时,直接 return json(['msg'=>'服务繁忙'], 429),避免静默放行
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!











