swoole事件轮询必须显式调用start()才能启动,否则epoll_ctl未初始化、epoll_wait无法执行,导致端口未监听、回调不触发、定时器失效等问题;所有配置和注册须在start()前完成。

不调用 start(),Swoole 的事件轮询根本不会跑起来——这是 90% 的 epoll 相关问题根源。
为什么 start() 必须显式调用且不能晚于配置
epoll 实例在 Swoole 内部由 Reactor 线程管理,但它的生命周期完全绑定在 start() 调用之后。没调用它,epoll_ctl 就没被初始化,epoll_wait 更无从谈起。
- 常见错误现象:
PHP Fatal error: Swoole\Server::start(): server is not configured或程序静默退出,监听端口根本没起来 - 必须在
start()前完成:set()(含reactor_num、worker_num)、on()回调注册、addProcess()等所有初始化动作 - 如果先
go()启动协程再start(),协程会卡在Co::sleep()或 I/O 操作上,因为底层 event loop 还没启动,无法调度 - CLI 下
start()是阻塞调用;一旦返回,说明进程已退出或被 kill,轮询早已终止
onReceive/onConnect 回调反复触发失败的典型原因
这些回调本就是为高频复用设计的,每次新连接或数据到达都会被重新调用。所谓“只执行一次”,其实是轮询卡死或回调内部阻塞了 Reactor 线程。
Swoole 6.1.1 是一个专为 PHP 设计的高性能事件驱动并发网络引擎。作为稳定版,它修复了编译时对 zlib 依赖的缺失及 curl 模块的内存安全风险。该版本支持协程、多线程与多进程架构,内置 TCP/HTTP/WebSocket 服务器,能够显著提升 PHP 在微服务、实时通信等场景下的执行效率与并发能力。
- 最常见诱因:在回调里用了同步 I/O,比如
file_get_contents()、curl_exec()、mysqli_query()—— 它们会让当前 Reactor 线程彻底阻塞,后续事件全部积压 - 协程内未
await协程化操作(如Co::readFile()返回的是协程对象,直接 echo 不会触发执行) - 回调函数里抛出未捕获异常,导致该连接上下文崩溃,Reactor 不再向其派发事件
- 误把
onRequest(HTTP)和onReceive(TCP)混用:HTTP 请求走onRequest,底层 TCP 数据流仍由onReceive处理,但框架层已封装,不该手动干预
tick() 和 after() 不触发?先确认 event loop 是否真在跑
这两个 API 依赖 Swoole 内部的时间轮(time wheel),而时间轮只有在 epoll_wait 循环持续运行时才推进。它不是独立线程,也不后台常驻。
-
tick(1000, ...)必须在start()之前注册,否则报SWOOLE_ERR_INVALID_OPERATION - 注册后仍不触发?检查是否在
start()后立刻exit()或die(),或者主协程提前结束(如没go()任何长期任务) -
tick(1)在高并发下极易挤占 CPU,Reactor 线程忙于处理定时器,没空响应网络事件;应改用defer()或降低频率 - 回调中若 throw 异常且未 try/catch,该 timer 会被永久移除,不会自动重装
epoll LT/ET 模式对 Swoole 用户透明,但非阻塞 socket 是硬要求
Swoole 默认使用 epoll ET 模式 + 非阻塞 socket,这是高性能前提。你不需要手动 epoll_ctl,但必须理解它带来的行为约束。
- Swoole 自动设置 socket 为非阻塞(
O_NONBLOCK),若你手动用stream_socket_server()创建 socket 再传入 Swoole,务必自己设非阻塞,否则 ET 模式下recv()可能无限返回EAGAIN - LT 模式下,只要 socket 缓冲区有数据,
epoll_wait就持续通知;ET 模式只在状态变化时通知一次,所以必须循环读到EAGAIN才算收完 - Swoole 已帮你封装好这一逻辑:只要用
co::read()或$server->send(),它内部会自动处理 ET + 非阻塞的边界条件 - 真正容易踩坑的是自定义协议解析——若在
onReceive中手动fread()或socket_recv(),就绕过了 Swoole 的非阻塞封装,极易卡死
event loop 不是“开了就能用”的黑盒,它是 Reactor 线程 + epoll_wait + 时间轮 + 协程调度器的协同体。任何一个环节断开(比如忘了 start()、回调里写了 sleep(1)、或协程没挂起),整个轮询就停摆。调试时优先确认 ps aux | grep swoole 是否真有 worker 进程在跑,再看 lsof -i :端口 是否监听成功——别急着查代码逻辑,先验证轮询本身有没有活过来。










