正文
所以综合考虑服务器性能和网络 IO 等因素,在 API 元数据读取采用基于 ZooKeeper 的统一配置,并自研实现多级缓存容灾架构方案,从 ZooKeeper、内存和本地文件等进行多级缓存,同时支持数据变更时即时同步,以及系统宕机网络异常等情况下的数据自动容灾等策略。
以读为例
,网关首先从内存中读取配置,如无数据,从 ZooKeeper 读取,读取后同步到内存,并异步保存本次快照。如果 ZooKeeper 数据变更,通过监听 ZooKeeper 的 DataChangeWatcher 变更同步数据。如果 ZooKeeper 宕机,重启服务器,系统还可以通过本地快照恢复最近一次的元数据配置。
API HTTP 网关通过接口提供服务调用获取请求数据的,而搭建客户端与服务平台的 TCP 网关的双向通道,以保持客户端与服务平台的会话状态,则可以在 HTTP 网关基础上提供更多、更灵活的技术实现和业务实现。
在业务服务调用上通过 HTTP 网关,在平台服务调用上则通过 TCP 网关,实现平台与业务解耦,并且平台采用 TCP 通道还可以增加对平台的控制力,在此背景下诞生了 TCP 网关。
TCP 网关采用长连接通道,实现全双工会话。TCP 网关采用 Netty 作为 TCP 容器,在 ChannelPipe中加载自定义 ChannelHandler,构建 Container 容器,将每个 TCP Connection 封装到一个 Session 会话中,保存在 Container 容器中,由 Container 容器构建 Session 会话层提供逻辑层请求调用。
自研构建 Session 会话层是因为 HTTP 属于 OSI 的应用层,而 TCP 属于 OSI 的传输层,面向连接的编程极大的增加程序复杂度,所以将 Connection 封装在每一个 Session 会话里,再以微服务的方式提供服务调用,极大的精简了 TCP 编程。
客户端与服务端通过 TCP 长连接进行通信,但在中国复杂的网络环境下,移动客户端可能由于网络抖动、弱网络情况下,遭遇非正常网络闪断,如何处理断开后的断线重连,保证客户端与服务端的通讯稳定呢?
客户端每通过 TCP 与服务端进行一次建连,都会在服务容器里创建一个 Session 会话,该会话保存 Connection 的句柄,对应 Netty 的一个 Channel 通道。建连成功后,通过定时的心跳保持 Channel 属于 Active 活跃。但客户端进入弱网络环境下,客户端可能已经掉线,但并未向服务端主动发送关闭 Channel 请求,而服务端仍认为该 Channel 仍存活。直到在由服务端的会话存活检测机制检测到 Channel 已经 InActive,才会由服务端销毁该 Channel。
服务端的会话存活检测是 5 分钟一次,所以存在客户端掉线后,在 5 分钟内又重新建连,而这时服务端的建连逻辑,不是重新创建一个 Session,而是去寻找上一次的 Session,并更新标识存活。具体的实现是在每次建连的 Channel 里存入 SessionId,当网络闪断后,判断 Channel 是否存在 Session,之所以实现是得益于 Netty 的 ChannelHandlerContext,可以存储一个自定义属性到 Channel 的上下文中。
当然,TCP 网关一定是集群,所以,断线重连也是极有可能请求到不同的服务器上,而这种情况按照新 Connection 创建的 Session 处理,只有出现重连到同一服务器时,才需要考虑上述的处理逻辑。