onreceive callback is not set是启动校验失败,因未显式注册onreceive回调;connection refused主因是服务未驻留;task method is not available是task_worker_num=0导致无task进程。

绝大多数 Swoole 报错不是代码写错了,而是进程模型、生命周期或配置语义被误解了。 比如 onReceive callback is not set 不是漏写了函数,是根本没注册;Connection refused 很少是端口填错,通常是服务没真正驻留;task method is not available 也不是调用姿势不对,是 task_worker_num 被设成了 0。
onReceive callback is not set 怎么快速定位
这个报错在 $serv->start() 执行瞬间就抛出,说明服务连监听都没开始建——根本没走到配置加载之后的阶段。它和 worker_num 大小、端口是否空闲完全无关。
- 必须显式注册:
$serv->on('receive', function($serv, $fd, $reactor_id, $data) { });,哪怕函数体为空也得有 - 不能靠“继承父类自动注册”或“配置数组里写回调名字符串”,Swoole 不支持这种写法
- 如果用的是
Swoole\WebSocket\Server,要注册的是onMessage,不是onReceive;混用会直接报这个错 - 检查是否在
onStart或onWorkerStart里提前exit或抛异常,导致后续注册逻辑没执行
Connection refused 连不上但 telnet 能通
这说明 TCP 层通了,但应用层没响应。常见于 Docker、Nginx 反代、或 daemonize 没生效的场景。
Swoole 6.1.1 是一个专为 PHP 设计的高性能事件驱动并发网络引擎。作为稳定版,它修复了编译时对 zlib 依赖的缺失及 curl 模块的内存安全风险。该版本支持协程、多线程与多进程架构,内置 TCP/HTTP/WebSocket 服务器,能够显著提升 PHP 在微服务、实时通信等场景下的执行效率与并发能力。
- 确认
daemonize => 1已设置(代码里或配置数组),否则脚本执行完进程就退出 - Docker 中检查
bind地址:必须是0.0.0.0:9501,不能是127.0.0.1:9501,后者容器内可通,宿主机不可达 - Nginx 反代 WebSocket 时,缺任意一行都会握手失败:
proxy_http_version 1.1、proxy_set_header Upgrade $http_upgrade、proxy_set_header Connection "upgrade" - 启动后立刻用
ps aux | grep php确认进程仍在,别只信命令回车成功
task method is not available 是配置还是代码问题
这是明确的设计限制,不是 bug。当 task_worker_num = 0 时,Swoole 根本不启 task 进程,$server->task() 必然失败。
- 不要在
onReceive或onRequest里无条件调task(),先判断if ($server->taskworker_num > 0) - 如果只是想异步执行一段逻辑,且不需要跨进程隔离,直接用
go(function () { ... })更轻量 - 注意
task_worker_num是数值型配置项,写成字符串"0"也会被当成 false,导致 task 进程不启动 - 上线前加个启动校验:
if (!$server->taskworker_num) { throw new RuntimeException('task_worker_num is 0, task() unavailable'); }
协程里 PDO/Redis 连接复用为什么总断
因为 Swoole Worker 是长生命周期的,而传统客户端(如 ext-redis、PDO)的连接默认有 idle timeout,Broker 或 MySQL 服务端主动断开后,PHP 层不会自动重连。
- 别在
onRequest里每次 new Redis —— 改用单例 + 健康检查,发请求前调$redis->ping() === false就重建 - MySQL 推荐用
mysqlnd_ms或swoole_mysql(已废弃)替代原生 PDO,或自己封装带重连的连接池 - Redis 启用
['scheme' => 'tcp', 'retry_interval' => 100]参数,但注意这仅对连接建立阶段有效,不保活已建立连接 - 最关键的:所有连接对象必须绑定到当前协程上下文,不能存在 static 或全局变量跨协程共享
最容易被忽略的一点:很多报错看似随机,其实是多个配置项语义冲突导致的,比如 enable_coroutine = true 却把 worker_num 设成 32,结果 CPU 空转、延迟飙升却查不到慢日志——这不是某一行代码的问题,是整个并发模型被写反了。










