必须在onconnect阶段用redis实时限频封禁高频ip:先incr再expire设置5秒窗口,超阈值即$connection->close();redis连接须预置onworkerstart,禁用onconnect内new;nginx限流对非http连接无效,防护主责在workerman自身。

Workerman 本身不拦截连接,恶意连接拖垮服务器的根因是服务端未做连接准入控制——必须在 onConnect 阶段就完成 IP 限频、连接数限制和基础校验,否则等进到 onMessage 就晚了。
如何在 onConnect 中实时封禁高频重连 IP
客户端网络闪断后集体重试,会瞬间打爆服务端。不能等请求进来再处理,必须在连接建立的第一时间判断是否放行。
- 用
Redis记录每个 IP 的 5 秒内连接次数,超阈值(如 10 次)直接调用$connection->close() - 避免在
onConnect里 new Redis(),应提前在onWorkerStart初始化并存入$GLOBALS['redis'] - 记录需带过期时间:
$redis->incr("conn:ip:{$ip}"); $redis->expire("conn:ip:{$ip}", 5); - 若 Redis 不可用,降级为进程内计数(
static $counter = []),但仅作兜底,不可长期依赖
为什么不能只靠 Nginx limit_conn 做防护
Nginx 的 limit_conn 只对 HTTP/HTTPS 有效,对纯 TCP 或 WebSocket 连接(如 ws://、text://)完全不生效——Workerman 启动的是独立监听进程,Nginx 根本看不到这些连接。
- 如果你用 Nginx 做反向代理转发 WebSocket,那
limit_req可控 Upgrade 请求频率,但无法限制 Upgrade 成功后的长连接数量 - 真正管连接数的,只有 Workerman 自己:通过
$worker->count控制 Worker 进程数,再配合每个进程内onConnect的 IP 级限流 - 务必关闭调试模式:
Worker::$daemonize = true;+ini_set('display_errors', 'Off');,防止错误泄露暴露逻辑
TextProtocol 下如何防超长字符串耗尽内存
默认的 TextProtocol 对单行长度无约束,一个不含换行符的 500MB 数据包就能让 PHP 进程 OOM 崩溃。
- 必须显式配置
protocol_args:'max_package_size' => 1024 * 1024(1MB) +'strict_mode' => true - 不能只写
$worker->protocol = TextProtocol::class;,必须传参,否则配置无效 - 更稳妥的做法是弃用
TextProtocol,改用FrameProtocol:自带 4 字节长度头,天然防粘包、防超长、无需手动解析 - 若协议已固定,至少在
onMessage开头加if (strlen($data) > 1024 * 1024) { $connection->close(); return; }
真正容易被忽略的点是:所有连接级防护逻辑(IP 限频、协议校验、连接池初始化)都必须放在 onWorkerStart 和 onConnect,而不是等消息来了才启动;一旦放进 onMessage,攻击者已经拿到连接句柄,防线就破了。











