TCP慢启动因初始拥塞窗口小导致首屏消息延迟100–300ms,影响IM/推送实时性;可通过启用TFO跳过SYN重传、复用长连接避免频繁新建来优化。

Workerman里TCP慢启动为什么会影响实时性
Workerman默认新建连接走标准TCP三次握手+慢启动,前几个RTT内窗口只允许发少量包(初始cwnd=10 MSS),对IM、推送类服务来说,首屏消息或心跳响应可能卡在100–300ms,尤其在高丢包或高RTT链路上更明显。这不是Workerman代码问题,而是Linux内核行为,但Workerman的连接生命周期管理方式会放大它。
怎么用tcp_fastopen绕过前两次握手
启用TFO能跳过SYN重传等待,在首次握手就捎带数据,前提是客户端和服务端都支持且开启。Workerman本身不直接控制socket选项,得靠系统层配置和Worker启动前的手动设置:
- 确认内核支持:
cat /proc/sys/net/ipv4/tcp_fastopen返回值含1(服务端)和2(客户端)才有效 - 在Worker实例化前加socket选项(PHP 7.4+):
$context = stream_context_create([ 'socket' => ['so_reuseport' => true, 'tcp_nodelay' => true] ]); $worker = new Worker('tcp://0.0.0.0:2345', $context); // 启动前手动setsockopt(需root权限) $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); socket_set_option($socket, IPPROTO_TCP, TCP_FASTOPEN, 1); socket_close($socket); - 注意:TFO对首次连接有效,重连仍走标准流程;且Nginx反代或某些防火墙会拦截TFO Cookie,实测前务必用
ss -i看wscale和tfo字段
怎么让新连接复用已有的大窗口
慢启动本质是保守试探带宽,但Workerman长连接场景下,多数连接存活时间远超RTT,完全可以用“热连接”经验加速新连接。关键不是改代码,而是避免无意义的新建连接:
- 禁用客户端频繁重连逻辑,强制使用
$connection->keepConnection = true保持连接 - 在
onConnect里记录连接创建时间,用Worker::addTimer()定期检查,对time() - $connection->connectTime 的连接不主动close - 前端WebSocket应设
reconnectInterval≥5秒,避免雪崩式重连——一次批量重连会触发大量慢启动,比单个连接延迟更伤性能
为什么调大initcwnd没用还可能更慢
有人试过用ip route change强行提高initcwnd,但在Workerman里基本无效,原因很实在:
- Workerman监听socket是被动打开,路由表设置对accept后的fd不生效,得在每个新连接fd上单独
setsockopt(SOL_SOCKET, SO_SNDBUF) - 即使设了,Linux 4.15+内核会忽略非特权进程对initcwnd的修改,除非用
cap_net_admin能力或root运行 - 盲目堆大初始窗口会导致早期内核丢包率飙升(尤其WiFi或4G环境),反而触发更多重传,延迟波动更大
真正可控的点只有两个:减少连接新建频率 + 让TFO生效。其他所有“改内核参数加速慢启动”的操作,在Workerman这种用户态长连接框架里,要么不可行,要么副作用大于收益。

















