onBufferFull频繁触发说明发送缓冲区已失控,根本原因是发送节奏远超内核写出能力,导致缓冲区瞬时积压触顶并静默丢包;必须配合onBufferDrain闭环控制、业务层分片(≤64KB/次),并同步调优Linux内核TCP参数(如rmem_max/wmem_max≥4194304且开启tcp_window_scaling)。

为什么onBufferFull频繁触发说明发送缓冲区已失控
Workerman 的 onBufferFull 不是预警,而是丢包前最后一刻的“刹车失灵提示”。它被频繁触发,代表应用层发包节奏远超内核写出能力——TcpConnection::$maxSendBufferSize 默认 1MB,在高并发下连续 send() 大包(如 >100KB)或高频小包(毫秒级),缓冲区瞬间积压,很快触顶。更危险的是:一旦触发,后续仍调用 send(),数据会被静默丢弃,并最终触发 onError(错误码 2:“send buffer full and drop package”)。
常见误操作包括:
- 只注册 onBufferFull,却不配合 onBufferDrain 恢复发送
- 在 onBufferFull 中仅打日志或 return,未设状态标记暂停发送
- 误以为 $maxSendBufferSize 是硬边界,实际它只是软限制:只要还剩 1 字节空间,send($data) 就会把整块 $data 塞入,瞬时占用可能远超设定值
怎么正确设置 TcpConnection::$maxSendBufferSize 和响应逻辑
单纯调大 TcpConnection::$maxSendBufferSize(比如设为 4 * 1024 * 1024)只能延缓丢包,不能根治。关键是要闭环控制:
- 在
onBufferFull中立即设置标记(如$connection->isSending = false),停止向该连接继续send() - 在
onBufferDrain中恢复标记($connection->isSending = true),并检查待发队列是否非空 - 业务层主动分片:单次
send()控制在 64KB 以内,自带序号/校验,由客户端拼接;避免一次性传 512KB+ 大包 - 验证当前缓冲区真实积压量:在
onBufferFull里打印$connection->getSendBufferQueueSize(),你会发现它常远大于$maxSendBufferSize
Linux 内核 TCP 缓冲区必须配对,否则 Workerman 白调
Workerman 的 TCP 性能瓶颈 90% 不在 PHP 层,而在 /etc/sysctl.conf 里缓冲区参数没配对。核心陷阱是:net.core.rmem_max 和 net.core.wmem_max 会截断 tcp_rmem/tcp_wmem 的第三项(最大值)。如果前者更小,后者形同虚设。
查清真实上限:
- 运行 sysctl net.ipv4.tcp_rmem net.ipv4.tcp_wmem net.core.rmem_max net.core.wmem_max
- 重点对比:net.core.rmem_max ≥ tcp_rmem 第三项(如 4194304);同理检查发送侧
- 用 ss -i dst 查单连接的 rcv_space/snd_space,若长期卡在 256KB 或 512KB,就是被截断了
推荐配置(长连接、小包高频场景):
- net.core.rmem_max = 4194304(4MB)
- net.core.wmem_max = 4194304
- net.ipv4.tcp_rmem = 4096 65536 4194304
- net.ipv4.tcp_wmem = 4096 32768 4194304
- 必须开启 net.ipv4.tcp_window_scaling = 1,否则窗口无法突破 64KB
光调缓冲区不够,还得同步加固连接队列和资源上限
只改 maxSendBufferSize 和 sysctl 缓冲区,Workerman 依然会静默丢连接或爆内存。必须同步调整:
-
net.core.somaxconn = 65535:全连接队列上限,但 Workerman 默认 backlog 是系统值(常为 128),最终生效的是min(backlog, somaxconn);需确认ss -lnt的 Send-Q 是否真达到预期 -
net.ipv4.tcp_max_syn_backlog = 65535:防 SYN 半连接堆积,否则客户端建连超时 -
fs.file-max = 2097152+/etc/security/limits.conf中nofile设为 1048576:每个连接占一个 fd,不够会报 “too many open files” -
net.ipv4.tcp_tw_reuse = 1:Workerman 主动发起下游连接(如 HTTP 调用)时必需,防 “Cannot assign requested address”
复杂点在于:这些参数之间存在隐式依赖。比如调大 somaxconn 后,若 file-max 没跟上,新连接进不来;而 tcp_window_scaling 关闭时,再大的 rmem_max 也无效。它们不是独立开关,是一组协同生效的齿轮。











