简介
本指南面向 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 分布等指标,持续优化策略。