在设计通讯协议时我们参考了 MQTT 规范,拓展了认证和鉴权设计,完成了业务消息的隔离与解耦,保证了一定程度的传输可靠性 。同时保持了与 MQTT 协议一定程度上兼容,这样便于我们直接使用 MQTT 的各端客户端实现,降低业务方接入成本 。
我们怎么设计系统架构?
在设计项目整体架构时,我们优先考虑的是:
- 可靠性
- 水平扩展能力
- 依赖组件成熟度
为了保证可靠性,我们没有考虑像传统长连接系统那样将内部数据存储、计算、消息路由等等组件全部集中到一个大的分布式系统中维护,这样增大系统实现和维护的复杂度 。我们尝试将这几部分的组件独立出来,将存储、消息路由交给专业的系统完成,让每个组件的功能尽量单一且清晰 。
同时我们也需要快速的水平扩展能力 。互联网场景下各种营销活动都可能导致连接数陡增,同时发布订阅模型系统中下发消息数会随着 Topic 的订阅者的个数线性增长,此时网关暂存的客户端未接收消息的存储压力也倍增 。将各个组件拆开后减少了进程内部状态,我们就可以将服务部署到容器中,利用容器来完成快速而且几乎无限制的水平扩展 。
最终设计的系统架构如下图:
文章插图
系统主要由四个主要组件组成:
- 接入层使用 OpenResty 实现,负责连接负载均衡和会话保持
- 长连接 Broker,部署在容器中,负责协议解析、认证与鉴权、会话、发布订阅等逻辑
- redis 存储,持久化会话数据
- Kafka 消息队列,分发消息给 Broker 或业务方
我们如何构建长连接网关?
接入层
【知乎千万级高性能长连接网关是如何搭建的】OpenResty 是业界使用非常广泛的支持 Lua 的 Nginx 拓展方案,灵活性、稳定性和性能都非常优异,我们在接入层的方案选型上也考虑使用 OpenResty 。
接入层是最靠近用户的一侧,在这一层需要完成两件事:
- 负载均衡,保证各长连接 Broker 实例上连接数相对均衡
- 会话保持,单个客户端每次连接到同一个 Broker,用来提供消息传输可靠性保证
常见的四层负载均衡策略是根据连接来源 IP 进行一致性 Hash,在节点数不变的情况下这样能保证每次都 Hash 到同一个 Broker 中,甚至在节点数稍微改变时也能大概率找到之前连接的节点 。
之前我们也使用过来源 IP Hash 的策略,主要有两个缺点:
- 分布不够均匀,部分来源 IP 是大型局域网 NAT 出口,上面的连接数多,导致 Broker 上连接数不均衡
- 不能准确标识客户端,当移动客户端掉线切换网络就可能无法连接回刚才的 Broker 了
最后我们选择利用 Nginx 的 preread 机制实现七层负载均衡,对后面长连接 Broker 的实现的侵入性小,而且接入层的资源开销也小 。
Nginx 在接受连接时可以指定预读取连接的数据到 preread buffer 中,我们通过解析 preread buffer 中的客户端发送的第一个报文提取客户端标识,再使用这个客户端标识进行一致性 Hash 就拿到了固定的 Broker 。
发布与订阅我们引入了业界广泛使用的消息队列 Kafka 来作为内部消息传输的枢纽 。
前面提到了一些这么使用的原因:
- 减少长连接 Broker 内部状态,让 Broker 可以无压力扩容
- 知乎内部已平台化,支持水平扩展
- 使用消息队列削峰,避免突发性的上行或下行消息压垮系统
- 业务系统中大量使用 Kafka 传输数据,降低与业务方对接成本
发布长连接 Broker 会根据路由配置将消息发布到 Kafka Topic,同时也会根据订阅配置去消费 Kafka 将消息下发给订阅客户端 。
推荐阅读
- 高级程序员最爱用的8款代码编辑器,你用哪几个?
- 淘宝超级推荐怎么出价
- 初级茶艺师基础常识,南京茶艺师培训
- 超级推荐基础出价多少合适 超级推荐的出价是怎么计算的
- 直通车好还是极速推好 直通车和超级推荐的区别
- SUV|30万买吗?比亚迪2022款唐EV新车到店:S级豪华大6座、续航730km
- 中级会计师报名条件有哪些
- 淘宝直播超级推荐有用吗 淘宝超级推荐效果怎么样
- 超级推荐展现量低怎么回事 超级推荐修改出价从哪里进入
- 女性冬季中如何做好养生 八事项千万不能大意