swoole\table + 固定窗口计数器是最轻量、最可控的单机限流方案,onrequest中拦截可实现零延迟拒绝,需哈希ip防key冲突、预留table容量并由worker 0定时清理。

直接用 Swoole\Table + 固定窗口计数器是最轻量、最可控的方案,单机高并发下几乎零延迟。别一上来就上 Redis,除非你明确需要跨进程/多实例共享状态。
onRequest 里做拦截判断最有效
限流必须在请求刚进来、还没进业务逻辑前拦住,onRequest 是唯一可靠位置。Swoole HTTP Server 的这个回调是同步执行的,不涉及协程调度开销,判断快、无副作用。
- 不要在中间件或路由层做——那已经晚了,内存和数据库连接可能已分配
- 避免在
onWorkerStart或定时器里做统计汇总——实时性差,且无法按请求粒度拒绝 - 若用了 EasySwoole/Laravel-Swoole,确保你的限流逻辑注册在
EventRegister::onRequest阶段,而非框架的「请求生命周期」中
Swoole\Table 存 IP 计数要防 key 冲突
Swoole\Table 不支持字符串主键,必须自己哈希映射成固定长度 key,否则 set() 会失败或覆盖。
Swoole 6.1.1 是一个专为 PHP 设计的高性能事件驱动并发网络引擎。作为稳定版,它修复了编译时对 zlib 依赖的缺失及 curl 模块的内存安全风险。该版本支持协程、多线程与多进程架构,内置 TCP/HTTP/WebSocket 服务器,能够显著提升 PHP 在微服务、实时通信等场景下的执行效率与并发能力。
- 用
substr(md5($ip), 0, 16)比直接截取$ip更安全,避免192.168.1.1和192.168.1.10哈希后前缀重复 -
Table初始化时size要留余量:1024 × 128 是常见起步值,但若日均活跃 IP 超 5 万,需调大,否则set()返回 false - 记得为每个 key 设置
lastAccessTime字段,否则无法做滑动窗口(固定窗口可省略)
固定窗口 vs 滑动窗口:选错算法会导致漏放或误杀
固定窗口(如“每分钟最多 60 次”)实现简单,但存在临界问题:用户在第 59 秒发 60 次,第 60 秒又发 60 次,实际 2 秒内 120 次——这在秒杀、短信接口里不可接受。
- 真要防临界爆发,必须用滑动窗口:每次请求查
ZCOUNT(Redis Sorted Set)或遍历Table中lastAccessTime > time() - 60的记录——后者 CPU 开销明显上升,慎用于 QPS > 5k 场景 - 令牌桶适合 API 网关类场景,但
Swoole\Table实现时要注意多进程写竞争:count和lastFillTime更新必须原子,推荐用cas伪指令或直接切 Redis+Lua - 漏桶不适合 HTTP 接口限流——它强制匀速输出,而 HTTP 请求天然离散,容易造成大量 429
真正容易被忽略的是清理机制:固定窗口必须定时清空旧数据,但 swoole_timer_tick 在多 worker 下会重复触发。正确做法是只让 worker_id === 0 的进程执行 clear(),或改用 Redis 的 key 自动过期(EXPIRE)代替手动清理。










