知乎千万级高性能长连接网关是如何搭建的

来源:https://www.xttblog.com/?p=4875
实时的响应总是让人兴奋的,就如你在微信里看到对方正在输入,如你在王者峡谷里一呼百应,如你们在直播弹幕里不约而同的 666,它们的背后都离不开长连接技术的加持 。
每个互联网公司里几乎都有一套长连接系统,它们被应用在消息提醒、即时通讯、推送、直播弹幕、游戏、共享定位、股票行情等等场景 。而当公司发展到一定规模,业务场景变得更复杂后,更有可能是多个业务都需要同时使用长连接系统 。
业务间分开设计长连接会导致研发和维护成本陡增、浪费基础设施、增加客户端耗电、无法复用已有经验等等问题 。共享长连接系统又需要协调好不同系统间的认证、鉴权、数据隔离、协议拓展、消息送达保证等等需求,迭代过程中协议需要向前兼容,同时因为不同业务的长连接汇聚到一个系统导致容量管理的难度也会增大 。
经过了一年多的开发和演进,经过我们服务面向内和外的数个 App、接入十几个需求和形态各异的长连接业务、数百万设备同时在线、突发大规模消息发送等等场景的锤炼,我们提炼出一个长连接系统网关的通用解决方案,解决了多业务共用长连接时遇到的种种问题 。
知乎长连接网关致力于业务数据解耦、消息高效分发、解决容量问题,同时提供一定程度的消息可靠性保证 。
我们怎么设计通讯协议?业务解耦支撑多业务的长连接网关实际上是同时对接多客户端和多业务后端的,是多对多的关系,他们之间只使用一条长连接通讯 。
知乎千万级高性能长连接网关是如何搭建的

文章插图
 
这种多对多的系统在设计时要避免强耦合 。业务方逻辑也是会动态调整的,如果将业务的协议和逻辑与网关实现耦合会导致所有的业务都会互相牵连,协议升级和维护都会异常困难 。
所以我们尝试使用经典的发布订阅模型来解耦长连接网关跟客户端与业务后端,它们之间只需要约定 Topic 即可自由互相发布订阅消息 。传输的消息是纯二进制数据,网关也无需关心业务方的具体协议规范和序列化方式 。
知乎千万级高性能长连接网关是如何搭建的

文章插图
 
权限控制我们使用发布订阅解耦了网关与业务方的实现,我们仍然需要控制客户端对 Topic 的发布订阅的权限,避免有意或无意的数据污染或越权访问 。
假如讲师正在知乎 Live 的 165218 频道开讲,当客户端进入房间尝试订阅 165218 频道的 Topic 时就需要知乎 Live 的后端判断当前用户是否已经付费 。这种情况下的权限实际上是很灵活的,当用户付费以后就能订阅,否则就不能订阅 。权限的状态只有知乎 Live 业务后端知晓,网关无法独立作出判断 。
所以我们在 ACL 规则中设计了基于回调的鉴权机制,可以配置 Live 相关 Topic 的订阅和发布动作都通过 HTTP 回调给 Live 的后端服务判断 。
知乎千万级高性能长连接网关是如何搭建的

文章插图
 
同时根据我们对内部业务的观察,大部分场景下业务需要的只是一个当前用户的私有 Topic 用来接收服务端下发的通知或消息,这种情况下如果让业务都设计回调接口来判断权限会很繁琐 。
所以我们在 ACL 规则中设计了 Topic 模板变量来降低业务方的接入成本,我们给业务方配置允许订阅的 Topic 中包含连接的用户名变量标识,表示只允许用户订阅或发送消息到自己的 Topic 。
知乎千万级高性能长连接网关是如何搭建的

文章插图
 
此时网关可以在不跟业务方通信的情况下,独立快速判断客户端是否有权限订阅或往 Topic 发送消息 。
消息可靠性保证网关作为消息传输的枢纽,同时对接业务后端和客户端,在转发消息时需要保证消息在传输过程的可靠性 。
TCP 只能保证了传输过程中的顺序和可靠性,但遇到 TCP 状态异常、客户端接收逻辑异常或发生了 Crash 等等情况时,传输中的消息就会发生丢失 。
为了保证下发或上行的消息被对端正常处理,我们实现了回执和重传的功能 。重要业务的消息在客户端收到并正确处理后需要发送回执,而网关内暂时保存客户端未收取的消息,网关会判断客户端的接收情况并尝试再次发送,直到正确收到了客户端的消息回执 。
知乎千万级高性能长连接网关是如何搭建的

文章插图
 
而面对服务端业务的大流量场景,服务端发给网关的每条消息都发送回执的方式效率较低,我们也提供了基于消息队列的接收和发送方式,后面介绍发布订阅实现时再详细阐述 。


推荐阅读