reactor不执行php代码,仅用c实现异步事件分发;worker才执行onconnect/onreceive等php回调,负责业务逻辑。两者分工明确:reactor专注网络层(监听、分发、心跳),worker专注业务层(解析、处理、响应)。

Reactor 不执行你的 PHP 代码,Worker 才是真正干活的——这是理解 Swoole 并发模型最关键的分水岭。很多性能问题、阻塞现象和配置误调,根源都出在混淆了这两者的职责边界。
Reactor 线程只做三件事,且全是 C 层非阻塞操作
Reactor 的唯一任务就是高效收包、分发连接、维护心跳。它不解析 HTTP,不调用 onReceive,也不执行任何你写的 PHP 函数。所有业务逻辑回调都在 Worker 进程中触发。
- 监听 socket 状态(可读/可写/错误),底层用
epoll_wait或kqueue实现 - 把就绪的连接或数据包按
dispatch_mode规则投递给对应 Worker 进程(比如dispatch_mode = 2表示按$fd取模) - 定期检查空闲连接,超时后主动断开(依赖
heartbeat_interval和时间轮)
常见错误现象:strace -p $pid 看到大量 epoll_wait 调用但 CPU 占用极低,top 显示 reactor_thread 几乎不耗资源,而 worker_proc 吃满 CPU——这说明瓶颈根本不在 Reactor,别去盲目调大 reactor_num 或 max_connection。
Worker 进程才是 PHP 代码的实际执行环境
Worker 接收来自 Reactor 的数据包后,才开始执行你注册的 onConnect、onReceive、onRequest 等回调。它的行为直接决定服务是否阻塞、是否能并发。
- 默认是同步阻塞模式:你在
onReceive里写sleep(1)或file_get_contents,整个 Worker 进程会卡住,无法处理其他请求 - 启用协程后可变为异步非阻塞,但必须显式调用
Swoole\Runtime::enableCoroutine()并指定合适的hook_flags(如SWOOLE_HOOK_FILE | SWOOLE_HOOK_CURL) -
dispatch_mode不影响 Reactor 本身,只控制数据包如何路由到哪个 Worker;同一个 TCP 连接的数据始终进同一个 Worker(除非dispatch_mode = 5)
容易踩的坑:在未启用 SWOOLE_HOOK_ALL 的情况下,以为用了协程就能避免阻塞,结果 PDO::__construct 或 curl_init 仍会同步阻塞整个 Worker——这类调用不会自动协程化,必须靠 hook 拦截并重定向到异步 I/O。
Reactor_num 设置过高反而降低性能
reactor_num 默认等于 CPU 核心数,这不是建议值,而是经过验证的合理起点。设得太高不仅没收益,还会引入额外开销。
- 多个 Reactor 线程争抢同一个 listen socket 的 accept 队列,Linux 内核 5.10 之前不支持
SO_REUSEPORT多队列分流,容易造成排队阻塞 - 线程切换成本上升,尤其在高连接频次场景下,上下文切换开销会明显拖慢新连接接入速度
- 观察
/proc/$pid/fd/下文件描述符数量,若接近max_connection但connection_num上不去,可能是 SYN queue 溢出(需调net.ipv4.tcp_max_syn_backlog),而非 Reactor 不够用
真正需要调 reactor_num 的情况极少,通常是压测时发现 accept_count 增速远低于连接建立速率,且确认不是 EMFILE(文件描述符耗尽)或 disable_accept = 1 导致的——这时才考虑微调,而不是一上来就堆数值。
Reactor 和 Worker 的分工本质是「网络层」和「业务层」的物理隔离。一旦你在 Worker 里做了本该交给 TaskWorker 或协程 IO 完成的事(比如同步 DB 查询、大文件读写),就等于把高速路当停车场用。最常被忽略的点是:Reactor 从不参与业务逻辑决策,它甚至不知道你跑的是 HTTP 还是 WebSocket;所有协议解析、状态维护、响应组装,全在 Worker 侧完成。











