在最近的项目中,我们线上脚本服务跑了一段时间后,日志里频繁出现如下警告:
WARNING swReactor_write (ERRNO 1008): socket#58 output buffer overflow起初看见这个报错有些懵,后来一番排查才确认根因:子进程给 worker 进程发送消息过大且过快,导致 Swoole 的输出缓冲区溢出。本文记录一下整个问题分析与解决过程。
1. 现象描述
服务运行一段时间后,Swoole 日志中出现大量
swReactor_write (ERRNO 1008): socket#XX output buffer overflow- 服务本身没有立刻崩溃,但某些业务消息丢失。
- 报错定位在 子进程与 worker 进程通信 的场景。
2. 背景知识
Swoole 在底层基于 异步事件循环 和 socket 通信。
- 当服务端往某个 socket 写数据时,Swoole 会先写入 输出缓冲区。
- 如果客户端(或目标进程)没有及时消费,缓冲区就会堆积。
- 一旦超过配置的阈值,就会报出
output buffer overflow警告。
换句话说,这个错误其实就是:
“写的速度太快,读的速度太慢,导致缓冲区爆了。”
3. 我们的踩坑点
在项目里,我们用 Swoole fork 子进程 的方式做数据采集,采集完的数据通过 管道消息 发送给 worker 进程。
问题在于:
- 子进程一次性推送的 数据包很大(可能几 MB 以上)。
- 推送 频率也很高,基本没有限速控制。
结果就是 worker 进程来不及消费,缓冲区瞬间被撑爆,触发了报错。
4. 解决方案
针对这个问题,我们从两个角度入手:
(1)调整 Swoole 配置
在 server->set() 里增加配置,扩大缓冲区上限:
$server->set([
'buffer_output_size' => 32 * 1024 * 1024, // 默认 2M,这里调大到 32M
'socket_buffer_size' => 128 * 1024 * 1024, // 默认 8M,这里调大到 128M
]);这样可以避免轻易触发溢出警告,适合大数据包场景。
(2)优化业务逻辑
更根本的办法是 控制写入速度 和 拆分大数据:
- 分片发送
将单个大数据包拆分成小块逐步推送,而不是一次性写入。 - 写入返回值检查
在调用send/push时判断返回值,如果false,说明缓冲区满了,需要重试或丢弃。 - 异步队列缓冲
子进程先写入本地队列,由 worker 消费,避免直接“洪水式”灌数据。
5. 总结
这次问题让我再次体会到:
在高并发/大数据场景下,消息队列和流控机制一定要设计好,不能指望底层缓冲区“兜底”。
最终的经验:
- 短期可以通过 增大 buffer 配置 解决。
- 长期要通过 限速、拆分、队列 来避免缓冲区压力。