文档目录
访问频率限制

简介

本指南面向 DouPHP 的访问频率限制(限流)能力,聚焦 throttle 配置项、限流中间件工作原理、不同端点的差异化策略、登录接口的暴力破解防护、文件后端存储机制与性能优化、DDoS 防护最佳实践,以及限流日志监控与分析方法。文档基于仓库中的实际代码实现进行说明,确保可落地、可操作。

项目结构

DouPHP 的限流能力由“配置 + 中间件 + 存储”三部分构成:

  • 配置:安全栈中的 throttle.store 与 throttle.default 定义在安全配置中。
  • 中间件:API 与前台分别提供定向限流中间件,继承统一的基类,按路由键匹配配额。
  • 存储:使用文件后端 ThrottleStore 以 JSON 文件形式记录每个限流键的计数与窗口重置时间。
graph TB
A["请求进入"] --> B["API/前台中间件链"]
B --> C["ThrottleMiddleware(定向限流)"]
C --> D["AbstractThrottleMiddleware(通用逻辑)"]
D --> E["ThrottleStore(文件存储)"]
E --> F["返回响应(允许/拒绝)"]

核心组件

  • 安全配置 security.throttle
    • store:限流计数文件存储目录,默认位于 STORAGE_PATH . 'cache/throttle/'。
    • default:全局默认限流规则;为 null 时仅对中间件中显式配额的敏感端点生效。
  • 抽象限流中间件 AbstractThrottleMiddleware
    • 提供统一处理流程:解析路由候选键、查找配额、计算限流键、调用存储 hit/tooMany/availableIn、超限拒绝。
    • 支持 routeLimit 覆盖与 resolveKey 自定义键生成。
  • API/前台定向限流中间件
    • api/middleware/ThrottleMiddleware:对登录、注册、短信验证码、公共匿名写接口等按 IP 限流,超限返回 JSON 429。
    • front/middleware/ThrottleMiddleware:对前台敏感端点按 IP 限流,超限抛出 DomainException 并跳转首页。
  • 文件存储 ThrottleStore
    • 每个限流键对应一个 JSON 文件,结构包含 count 与 reset。
    • 提供 hit、tooMany、availableIn、clear、purgeExpired 等方法,窗口到期自动重置。

架构总览

限流整体流程如下:

  • 请求进入后,先经过可信代理与 Host 校验(安全栈),再进入各端中间件链。
  • 定向限流中间件根据模块/动作/子段生成候选键,匹配内置配额表。
  • 若命中配额,则通过 ThrottleStore 统计当前窗口内次数,超过阈值即拒绝。
  • 未命中配额的路由直接放行,不增加额外开销。
sequenceDiagram
participant Client as "客户端"
participant API as "API 中间件"
participant Base as "抽象限流基类"
participant Store as "ThrottleStore"
participant App as "业务控制器"
Client->>API : HTTP 请求
API->>Base : 解析路由与候选键
Base->>Base : 查找配额(精确/父段/模块)
alt 命中配额
Base->>Store : hit(key, window)
Store-->>Base : count
alt count >= max
Base-->>Client : 429 + Retry-After
else 未超限
Base-->>App : 继续处理
App-->>Client : 正常响应
end
else 未命中配额
Base-->>App : 直接放行
App-->>Client : 正常响应
end

详细组件分析

配置项 throttle.store 与 throttle.default

  • store:限流计数文件存放目录。默认值为 STORAGE_PATH . 'cache/throttle/',可通过安全配置覆盖。
  • default:全局默认限流规则。设置为 null 表示默认不限流,仅对中间件中显式配额的敏感端点生效。

限流中间件工作原理

  • 候选键匹配:按 module/sub/action → module/sub → module/action → module 的顺序尝试匹配配额表。
  • 限流键生成:默认 key = module.action.ip,可在基类中重写 resolveKey 自定义。
  • 存储交互:hit 自增计数;tooMany 判断是否达到上限;availableIn 计算剩余秒数。
  • 拒绝策略:API 端返回 JSON 429 并设置 Retry-After;前台端抛出异常并跳转首页。
flowchart TD
Start(["进入中间件"]) --> Candidates["生成候选键列表"]
Candidates --> Match{"命中配额?"}
Match -- 否 --> Pass["放行到业务层"]
Match -- 是 --> Hit["store.hit(key, window)"]
Hit --> TooMany{"count >= max?"}
TooMany -- 是 --> Reject["拒绝(429/跳转)"]
TooMany -- 否 --> Continue["继续处理"]
Pass --> End(["结束"])
Reject --> End
Continue --> End

在不同端点应用不同的限流策略

  • API 端:对登录、注册、手机号登录、找回密码、短信验证码、公共匿名写接口、防伪查询、LLM 成本端点等按 IP 限流。
  • 前台端:对验证码、登录、注册、手机号登录、找回密码、公共表单提交、聊天相关端点等按 IP 限流。
  • 通过各自中间件的 $limits 表配置具体配额(max/window)。

登录接口暴力破解防护

  • 前端/接口层:ThrottleMiddleware 对 user/login_post 等登录提交端点按 IP 限流,防止高频尝试。
  • 业务层:UserAuthService::ipRateLimit 基于 user_log 表中指定时间窗内的失败次数判定是否触发 IP 限流。
  • 组合防护:中间件前置拦截高频请求,业务层进一步依据审计日志判定账号锁定或 IP 限流期。
sequenceDiagram
participant Client as "客户端"
participant Front as "前台中间件"
participant Auth as "用户服务"
participant DB as "user_log"
Client->>Front : POST /user/login_post
Front->>Front : 检查配额(user/login_post)
alt 超限
Front-->>Client : 429/跳转
else 未超限
Front->>Auth : ipRateLimited(limit, window)
Auth->>DB : 查询窗口内失败次数
DB-->>Auth : 次数
alt 达到阈值
Auth-->>Front : true
Front-->>Client : 拒绝(提示)
else 未达阈值
Auth-->>Front : false
Front-->>Client : 继续认证流程
end
end

文件后端存储的实现机制与性能考虑

  • 存储格式:每个限流键对应一个 JSON 文件,文件名由 md5(key).json 生成,内容包含 count 与 reset。
  • 窗口管理:hit 时若窗口已过期则重置计数;tooMany 仅在窗口期内判断;availableIn 计算剩余秒数。
  • 并发与清理:写入使用 LOCK_EX 保证原子性;purgeExpired 机会式清理过期文件。
  • 性能要点:
    • 高并发场景下频繁文件 IO 可能成为瓶颈,建议将 store 指向高性能磁盘或 SSD。
    • 合理设置 window 与 max,避免过多活跃键导致目录膨胀。
    • 定期执行 purgeExpired 减少无用文件数量。

依赖关系分析

  • 中间件依赖:API/前台 ThrottleMiddleware 均继承 AbstractThrottleMiddleware,复用通用逻辑。
  • 配置依赖:AbstractThrottleMiddleware 从 Config 读取 security.throttle.store 作为存储路径。
  • 存储依赖:ThrottleStore 负责文件读写,中间件通过 store() 获取实例。
  • 业务依赖:登录失败限流由 UserAuthService 基于 user_log 表实现,与中间件限流互补。
classDiagram
class AbstractThrottleMiddleware {
+handle(request, next)
+throttleFor(module, action, sub) array|null
+reject(retryAfter) void
+resolveKey(module, action, sub, ip) string
+candidates(module, action, sub) array
+store() ThrottleStore
}
class ApiThrottleMiddleware {
+throttleFor(module, action, sub) array|null
+reject(retryAfter) void
}
class FrontThrottleMiddleware {
+throttleFor(module, action, sub) array|null
+reject(retryAfter) void
}
class ThrottleStore {
+hit(key, window) int
+tooMany(key, max, window) bool
+availableIn(key) int
+clear(key) void
+purgeExpired() void
}
ApiThrottleMiddleware --|> AbstractThrottleMiddleware
FrontThrottleMiddleware --|> AbstractThrottleMiddleware
AbstractThrottleMiddleware --> ThrottleStore : "使用"

性能与存储考量

  • 存储路径选择:将 throttle.store 指向本地高性能磁盘或 SSD,降低 IO 延迟。
  • 窗口与配额调优:
    • 短窗口(如 60s)+ 小配额(如 5-10)用于高频敏感端点(登录、验证码)。
    • 长窗口(如 300s)+ 较大配额用于低频但重要的写操作(找回密码、申请分销)。
  • 文件清理:定期执行 purgeExpired 清理过期文件,避免目录膨胀影响性能。
  • 分布式扩展:当前实现为单机文件存储,多实例部署时需考虑共享存储或迁移至内存缓存(如 Redis)以提升吞吐与一致性。

故障排查指南

  • 常见问题
    • 限流未生效:确认中间件已注册且路由命中配额;检查 security.throttle.store 路径是否存在且可写。
    • 误判超限:核对 window 与 max 配置是否符合预期;查看 ThrottleStore 对应 JSON 文件的 reset 时间。
    • 登录失败仍被限流:检查 UserAuthService::ipRateLimit 的 limit 与 window 配置;确认 user_log 审计写入是否正常。
  • 定位步骤
    • 查看中间件日志或响应头 Retry-After,确认是否命中限流。
    • 检查 ThrottleStore 目录下对应 key 的 JSON 文件内容与时间戳。
    • 核对 API/前台中间件注册顺序,确保 TrustProxy 先于限流中间件执行,以保证 IP 可信。

结论

DouPHP 的访问频率限制通过“配置 + 中间件 + 文件存储”的组合实现了轻量、可控的定向限流能力。结合业务层的登录失败限流,可有效防护暴力破解与滥用。生产环境建议结合高性能存储、合理的窗口与配额配置、定期清理与监控,构建稳健的限流体系。

附录:配置模板与调优建议

常见限流场景配置模板

  • 登录接口(防暴力破解)
    • 中间件配额:user/login_post,max=5,window=60
    • 业务层限流:UserAuthService::ipRateLimit,limit=10,window=600
  • 验证码下发(防刷短信/邮件)
    • 中间件配额:captcha/verification,max=5,window=300
  • 公共匿名写接口(留言/咨询/订阅)
    • 中间件配额:guestbook/store、consultation/store、email/store,max=5-10,window=300
  • 聊天/LLM 成本端点(防滥用)
    • 中间件配额:chat/stream、chat/new_session,max=10-20,window=60

DDoS 防护最佳实践

  • 前置防护:在负载均衡/Nginx 层实施连接数与请求速率限制,减轻后端压力。
  • 可信代理:正确配置 trusted_proxies,确保 Request::ip() 获取真实客户端 IP。
  • 分层限流:中间件层做粗粒度限流,业务层做细粒度限流(如登录失败、验证码)。
  • 资源保护:对高成本端点(LLM、文件上传、搜索)设置更严格的配额。
  • 监控告警:对 429 比例、IP 集中度、窗口重置频率进行监控与告警。

限流日志监控与分析

  • 中间件响应:API 端 429 响应携带 Retry-After,便于客户端退避重试。
  • 业务审计:登录失败通过 user_log 记录,可用于 IP 限流判定与事后分析。
  • 存储观察:定期检查 ThrottleStore 目录的文件数量与大小,评估配额合理性。
  • 指标采集:采集命中率、拒绝率、平均窗口时长、热点 IP 分布等指标,持续优化策略。
添加日期:2026-10-05