Swoole 的事件循环需显式调用 start() 启动,否则进程直接退出;start() 前须完成配置与回调注册,且依赖底层 epoll/kqueue 实现永不退出的 while 循环。

event loop 是怎么启动的
Swoole 的事件循环不是自动运行的,Server 或 Client 实例创建后必须显式调用 start() 才真正进入循环。没调用它,代码就直接退出了——这是新手最常卡住的地方。
常见错误现象:PHP Fatal error: Swoole\Server::start(): server is not configured 或程序静默结束,没报错也没响应。
-
start()前必须完成所有配置(比如set()、on()注册回调) - 如果用
Coroutine::create()启动协程,但没在协程里调用go()或Co::sleep()等挂起操作,事件循环也不会推进 - CLI 模式下,
start()是阻塞调用;Web 服务器场景中,它等价于“接管整个进程的控制权”
onReceive/onConnect/onClose 回调为什么只执行一次就没了
这些回调不是“触发一次就销毁”,而是每次有新连接/数据到达时被反复调用。问题通常出在回调函数内部逻辑阻塞了事件循环,比如用了同步 I/O(file_get_contents、curl_exec)、死循环或未 await 协程。
使用场景:处理 TCP 流式数据时,onReceive 可能被高频触发;HTTP 请求则由 onRequest 接管,和底层 event loop 分离。
- 不要在回调里做耗时同步操作;改用
Co::curl_init()、Co::readFile()等协程版 API - 如果手动调用
yield或Co::sleep(),要确认当前上下文是否在协程中(Co::pid() !== 0) - PHP 8.1+ 中,若回调是匿名函数且捕获了大对象,可能引发内存泄漏,间接拖慢循环响应
为什么 $server->tick() 不生效
tick() 和 after() 依赖底层 event loop 的时间轮(time wheel)机制,只有在事件循环已启动且持续运行时才有效。单独 new Server 但没 start,或者 start 后立即 exit,tick() 就永远不会触发。
参数差异:tick(1000, ...) 表示每 1000ms 触发一次;after(2000, ...) 是单次延迟执行。两者都要求事件循环处于活跃状态。
- 确保
tick()调用发生在start()之前,否则会抛出SWOOLE_ERR_INVALID_OPERATION - 回调函数内不能 throw 未捕获异常,否则该 timer 会被永久移除(Swoole 默认不 recovery)
- 高频率
tick(1)在大量连接时可能挤占 CPU,建议用defer()替代短间隔轮询
协程调度器和 event loop 的关系到底是什么
从 v4.4 开始,Swoole 默认启用协程调度器(Co::set(['hook_flags' => SWOOLE_HOOK_ALL])),但它不是替代 event loop,而是“运行在 event loop 之上的用户态线程管理器”。event loop 负责 I/O 事件分发,协程调度器负责挂起/唤醒协程。
性能影响:开启全 hook 后,所有系统调用(如 fread、mysql_connect)都会自动协程化,但也会带来额外上下文切换开销。
- 非必要不全局 hook;可按需设置
SWOOLE_HOOK_TCP或SWOOLE_HOOK_SLEEP - 混合使用同步和异步代码时,
Co::exec()或exec()这类阻塞调用仍会卡住整个进程,和 event loop 无关 - 调试时用
Co::stats()查看当前协程数和调度延迟,比看 CPU 使用率更能定位 event loop 是否被压垮










