CodeIgniter 本身不内置全局限流,但可通过 RestServer 扩展或 CI4 的 Throttler 类实现;RestServer 支持 IP_ADDRESS、API_KEY、METHOD_NAME、ROUTED_URL 四种限流方式,各适用不同场景;CI4 Throttler 依赖缓存驱动(推荐 Redis),file 驱动易因文件锁失效;限流后需手动设置 Retry-After 响应头;limits 表需定时清理旧记录。

CodeIgniter 框架本身不内置全局限流,但通过 RestServer 扩展或 CI4 原生 Throttler 类,能快速实现稳定、可配的接口防刷。关键不是“能不能做”,而是选对机制、配对存储、避开缓存陷阱。
RestServer 的四种限流方式怎么选
RestServer 提供了四个值给 $config['rest_limits_method'],每种适用场景差异明显:
-
IP_ADDRESS:适合公开 API 或未登录用户,简单直接,但 NAT 环境下会误伤(比如整个公司共用出口 IP) -
API_KEY:必须配合$config['rest_enable_keys'] = true和keys表,适合已授权调用方,粒度细、可追溯 -
METHOD_NAME:按控制器方法名限流(如users_get),适合统一控制某类操作频次,不区分调用者 -
ROUTED_URL:精确到完整请求路径 + HTTP 方法(如GET /api/v1/users),适合灰度发布或临时封禁某个高危端点
注意:ROUTED_URL 在路由别名或参数化路由(如 /users/{id})下可能生成大量不同 key,导致 limits 表膨胀,慎用于动态路径。
CI4 的 Throttler 类为什么总失效
常见现象是 $throttler->check() 始终返回 true,或限流完全不触发——根本原因几乎都出在缓存配置上:
- CI4 的
Throttler依赖Cache服务,不是数据库,也不是 session - 默认
cache配置若为file驱动,高并发下会因文件锁竞争导致计数不准甚至跳过检查 - 必须确保
app/Config/Cache.php中启用且配置了可用驱动,推荐 Redis:'default' => 'redis' -
check()第一个参数是桶名(如 IP 地址),不能含非法字符;若传入空字符串或 null,会静默失败
示例正确写法:$throttler->check($request->getIPAddress(), 10, MINUTE) —— 不要漏掉 MINUTE 常量,它等价于 60,类型必须是整数秒。
限流后返回 429 却没带 Retry-After 头
RestServer 和 CI4 默认都不自动设置 Retry-After 响应头,前端无法知道还要等几秒。手动补上才完整:
- RestServer:在控制器中拦截响应,用
$this->response->setHeader('Retry-After', '60'),需配合自定义错误处理逻辑 - CI4 过滤器中:可在
before()返回 429 前加一行:Services::response()->setHeader('Retry-After', '1') - 更稳妥的做法是读取
Throttler::hit()返回的剩余等待秒数(需自行封装),否则硬编码容易和实际窗口脱节
没有 Retry-After,前端重试策略只能靠猜,容易引发雪崩式重试。
limits 表爆满或查询变慢怎么办
RestServer 的 limits 表默认不做自动清理,长期运行后可能达百万级记录:
- 表中
hour_started是时间戳(非 datetime),单位为秒,可用于定时清理旧数据:DELETE FROM limits WHERE hour_started - 务必给
uri和api_key字段加联合索引,否则SELECT ... WHERE uri = ? AND api_key = ?会全表扫描 - 如果用
ROUTED_URL,建议在应用层对 URI 做归一化(如去掉 query 参数中的签名、时间戳),否则相同逻辑接口会被视为多个 key
限流不是设完就完事,limits 表是状态中心,它的健康度直接影响整个限流系统的实时性和可靠性。











