Swoole自身不提供集群能力,高可用必须依赖外部组件协同实现;其worker_num、task_worker_num等参数仅优化单机性能,无法解决节点发现、服务注册与跨机故障转移问题。

Swoole 本身不提供集群能力,高可用必须靠外部组件协同实现;单靠改 $server->set() 参数无法达成真正的多节点容灾。
为什么不能只靠 Swoole 自身配置
Swoole 是一个常驻内存的 PHP 网络引擎,它的 worker_num、task_worker_num、enable_coroutine 等参数只影响单机进程模型,不涉及节点发现、服务注册或跨机故障转移。即使你把 worker_num 设成 100,它依然只是本机的一个孤立进程组——挂了就全挂,扩不了容,也切不了流。
常见误操作包括:
- 以为加了
max_request就算“高可用”(这只是防内存泄漏,和集群无关) - 在
onWorkerStart里硬编码其他 Swoole 节点 IP 做直连(破坏无状态性,且无法自动剔除宕机节点) - 用
Swoole\Process手动 fork 多个子进程模拟“集群”(仍是单机,没解决网络层隔离与故障隔离)
Nginx + 多实例是最简可行的负载入口
对中小规模业务,直接用 Nginx 做反向代理是最轻量、最可控的流量入口方案。关键不是“能不能”,而是“怎么配才不掉坑”:
- 后端 server 必须用
ip_hash或least_conn,避免轮询导致连接粘滞失效(尤其 WebSocket 场景) - 健康检查必须开启:
health_check interval=3 fails=2 passes=2,否则节点宕机后流量仍会打过去 - 每个 Swoole 实例监听不同端口(如
9501、9502),Nginx upstream 中显式列出,不要依赖 DNS 或动态解析 - 务必关闭 Swoole 的
daemonize,让进程由 systemd 或 supervisor 管理,便于日志捕获和异常重启
示例 Nginx upstream 片段:
upstream swoole_backend {
least_conn;
server 10.0.1.10:9501 max_fails=2 fail_timeout=5s;
server 10.0.1.11:9501 max_fails=2 fail_timeout=5s;
health_check interval=3 passes=2 fails=2;
}
Consul 注册必须带主动健康检查
仅调用 Consul::agent()->service()->register() 是不够的——它注册的是“我启动了”,不是“我能干活”。真实生产环境必须绑定 HTTP 或 TCP 类型的主动探活:
- 注册时传入
"check": {"http": "http://10.0.1.10:9501/health", "interval": "5s"},Swoole 需暴露该接口并返回{"status":"ok"} - 避免用
ttl类型心跳(需定时上报,失败即失联),HTTP 探活更可靠 - Consul 客户端必须部署在每台 Swoole 机器本地,不要集中部署在跳板机上(网络延迟会导致误判)
- 服务名建议带环境前缀,如
prod-swoole-api,避免 dev/staging 混淆
对应 Swoole 内的路由处理(以 HTTP Server 为例):
$server->on('request', function ($request, $response) {
if ($request->server['request_uri'] === '/health') {
$response->header('Content-Type', 'application/json');
$response->end('{"status":"ok"}');
return;
}
// ... 正常业务逻辑
});
状态共享必须剥离到外部存储
WebSocket 连接数、用户登录态、任务进度等数据一旦留在 Swoole 进程内存里,就天然无法跨节点同步。必须外置:
- 会话统一走
Redis,用SET key value EX 3600+ 唯一 session_id,禁止用 PHP$_SESSION - 长连接映射关系存 Redis Hash,例如
HSET user:1001 connections conn-abc conn-def,断连时主动清理 - 异步任务结果不要存在
task_worker进程内,完成立刻写入Redis或Kafka,由 Web 接口轮询或推送 - 避免用
Swoole\Table存跨 Worker 共享数据——它只在单机有效,且扩容时数据不迁移
容易被忽略的一点:Redis 连接必须用连接池(如 co\Redis 协程客户端),否则并发高时会耗尽连接数,表现为随机超时或 Connection refused。


















