知乎千万级长连接网关架构
知乎长连接网关要服务的不是单一业务,而是多个 App 和十几个形态各异的长连接业务。因此它的核心目标不是“为某个业务做一套专用长连接”,而是做一套共享、可水平扩展、业务解耦的通用长连接系统。
一、发布订阅模型
多客户端、多业务后端之间如果直接耦合,协议升级和业务变更会互相影响。知乎采用发布订阅模型:
- 客户端和业务后端只约定 Topic;
- 网关不关心业务协议和序列化方式;
- 消息以二进制数据传递。
二、权限控制
发布订阅模型解耦了业务协议,但还需要控制客户端对 Topic 的访问权限。网关无法知道某个 Live 是否已付费,这类业务状态只有业务后端知道。因此 ACL 支持回调鉴权:
- 配置 Topic 的订阅/发布动作通过 HTTP 回调给业务后端;
- 业务后端返回是否允许。
对于只需要当前用户私有 Topic 的场景,则通过 Topic 模板变量降低接入成本。例如 Topic 中包含用户名变量,网关可以独立判断是否允许用户订阅自己的 Topic。
三、消息可靠性
TCP 保证传输顺序,但不能解决客户端 Crash、业务异常、连接中断造成的数据丢失。知乎实现了回执和重传:
四、系统架构
设计优先考虑可靠性、水平扩展、依赖组件成熟度和简单性。系统拆成四个组件:
| 组件 | 技术 | 职责 |
|---|---|---|
| 接入层 | OpenResty | 负载均衡、会话保持 |
| Broker | 容器化服务 | 协议解析、认证鉴权、发布订阅 |
| 会话存储 | Redis | 持久化会话和未确认消息 |
| 消息枢纽 | Kafka | 业务后端与 Broker 之间解耦和削峰 |
接入层使用 Nginx preread 机制,读取客户端第一个报文中的客户端标识,再做一致性 Hash,保证同一个客户端稳定路由到同一个 Broker。这比按来源 IP Hash 更均匀,也能在网络切换后保持会话。
五、海量数据发布
Broker 根据路由配置发布到 Kafka Topic,同时根据订阅配置消费 Kafka 下发消息。路由规则和订阅规则分离后,可以组合出四种场景:
- 只发布不消费:数据上报;
- 发布后也消费:普通即时通讯;
- 只消费不下发:纯推送;
- 路由到一个 Topic,从另一个 Topic 消费:过滤或预处理。
这种组合足够覆盖绝大多数消息流转场景。
六、会话与 QoS
重要 Topic 消息会被标记 QoS 1。Broker 将消息写入 Redis 未确认队列后下发,收到客户端 ACK 再删除。这样单个 Broker 宕机后,客户端重连到其他 Broker 也能恢复 Session 数据。
订阅关系如果使用单个全局 HashMap 加锁,锁冲突会非常严重。知乎通过分片 HashMap 降低锁粒度:创建数百个 HashMap,按 Key Hash 取模后再操作,把全局锁分散到多把锁。
下发侧参考 TCP 滑动窗口设计并行发送机制:
正常场景下基于 TCP 的顺序性,无需逐条重试;只有重连后才对窗口内未确认消息重发,客户端保留窗口大小缓冲区去重。
七、总结
知乎长连接网关把协议设计、权限控制、可靠传输、水平扩展和会话持久化拆开处理。发布订阅解决业务解耦,Kafka 解决内部消息枢纽和削峰,Redis 解决会话持久化,分片锁和滑动窗口解决性能与吞吐。