centrifugo 单节点无法支撑百万级 websocket 连接,必须采用分布式架构并优化 redis 集群、连接路由、心跳与客户端重连协同;关键瓶颈在 redis 单点性能、配置不当及压测隔离缺失。

Centrifugo 能撑住百万级 WebSocket 连接,但**单节点永远做不到**——必须靠分布式架构 + 正确的资源协同策略。关键不在“能不能”,而在“怎么让 Redis、连接路由、心跳和客户端重连不互相拖垮”。
为什么不能只加节点?Redis 是第一个瓶颈
Centrifugo 多节点共享状态全靠 Redis PUB/SUB 和键值存储。如果 Redis 单点扛不住,所有节点会集体降级为“假分布式”:消息丢失、订阅不同步、连接数虚高但实际不可用。
-
必须用 Redis Cluster 或至少 Redis Sentinel,单实例 Redis 在 50 万连接时就容易出现
PONG timeout或redis: connection closed - 检查
redis.conf中的maxclients(建议 ≥ 200000)、tcp-keepalive(设为 60)和timeout(设为 0 禁用空闲断连) - Centrifugo 启动时若报
failed to subscribe to Redis channel,90% 是 Redis 连接池耗尽,需调大redis_pool_size配置项(默认 10,生产建议 50–100)
centrifugo --config 必须关闭的三个默认项
开箱即用的配置面向开发,不是百万连接场景。以下三项不关,集群会在 30 万连接后开始丢心跳、积压消息:
-
client_presence:设为false。开启后每个频道都要查 Redis 的 presence key,QPS 指数级上涨 -
history_size和history_ttl:设为0。历史消息缓存会吃光 Redis 内存并拖慢 PUB/SUB 投递 -
proxy_http_api:设为false。HTTP API 代理模式会把所有请求打到同一节点,破坏负载均衡
正确示例片段(config.json):
WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。
{
"redis_address": "redis://redis-cluster:6379",
"redis_pool_size": 80,
"client_presence": false,
"history_size": 0,
"history_ttl": 0,
"proxy_http_api": false
}
k6 压测脚本里最常漏掉的连接隔离逻辑
直接跑 k6 run misc/benchmarking/k6/benchmark.js 只能测单机极限。要模拟真实百万连接,必须让每台压测机使用**固定范围的用户 ID 和 Token**,否则所有连接会挤在同一个 Redis channel 上,触发锁竞争。
- 修改
benchmark.js中的user_id生成逻辑,例如:const userId = `${__ENV.TEST_NODE_ID}-${i}` - Token 必须带
info字段并嵌入user_id,否则 Centrifugo 无法做连接去重和路由 - 务必启用
--vus分片控制:比如 4 台机器,每台跑k6 run --vus 25000 --duration 10m benchmark.js,而非统一 10 万 VU - 压测中若频繁出现
websocket: close 1006 (abnormal closure),大概率是客户端没按user_id隔离,导致服务端连接复用冲突
HAProxy / nginx 做 WebSocket 负载均衡时的致命配置
很多团队用 HAProxy 做入口,却忽略 WebSocket 连接的长生命周期特性。错误配置会让连接在 60 秒后被静默中断,且客户端无法感知。
- HAProxy 必须禁用
http-check,改用option tcp-check,否则健康检查会主动断开空闲连接 -
timeout client和timeout server必须 ≥websocket_ping_interval+ 30 秒(例如 Ping 设 30s,则超时至少设 60s) - nginx 用户注意:
proxy_read_timeout和proxy_send_timeout必须设为 0(不限制),否则默认 60 秒会 kill 连接 - 所有 LB 层必须透传
X-Real-IP,Centrifugo 的max_conns_per_ip限流才有效,防止单 IP 扫描式打爆
真正卡住百万连接的,从来不是代码或语言,而是 Redis 的 PUB/SUB 批处理延迟、LB 的连接复用策略、以及压测时没做 user_id 隔离带来的 channel 锁争用——这些地方一错,节点再多也白搭。










