亿级流量架构演进实战 —— 架构演进构建TCP长连接网关 03

这不是一个讲概念的专栏,而且我也不擅长讲概念,每一篇文章都是一个故事,我希望你可以通过这些故事了解我当时在实际工作中遇到问题和背后的思考,架构设计是种经验,我有幸参与到多个亿级系统的架构设计中,有所收获的同时也希望把这些收获分享与大家。


2015年,我在实现了 APP 服务端的平台化转型之后,进一步开始对服务端架构的升级改造,故事由此继续。

承接上篇,API 网关经过两年的发展,逐渐演进拆分成了面向 ISV 的 API 开放网关和面向客户端的 API 服务网关,其中面向 ISV 的 API 开放网关延续了前两篇已经介绍过的技术栈,后期则主要在高并发、高可用、高性能上进行技术攻坚;而面向客户端的 API 服务网关则迎来了新一轮的蜕变。此时的 API 网关主要还是基于 HTTP 实现的,由于 HTTP 是无状态的,这使得服务端对客户端的中心管控问题就显得愈加凸显,尤为严重的是在上文提到的一些事故。由此构建有状态的 TCP 长连接 API 网关就成为解决该问题的一颗银弹,可喜的是,TCP 网关的双向通信不仅可以保持客户端与服务端的会话状态,实现有效管控,更在未来为重构消息 PUSH 系统提供了技术底座。

在2016年整体上线了基于 Netty4.x + Protobuf3.x 的 TCP 长连接网关,实现了支持 APP 上下行通信的高可用、高性能、高稳定的 TCP 网关,而其性能也较 HTTP 网关提升10倍以上,稳定性也远远高于 HTTP 网关。

1。Netty 框架

我们先来谈谈 Netty,谈起 Netty 现在大家都很熟悉了,它在很多中间件和平台架构里都有扮演很关键的角色,我最早了解到 Netty 是在阅读 dubbo 源码时。

Netty 是一个可用于快速开发可维护的高性能协议服务器和客户端的异步的事件驱动网络应用框架(引自 netty.io),就我个人理解,它在实现基于 TCP NIO 长链接的通信领域可以提供了强大的框架支持。所以,了解 Netty 是非常大有裨益的,推荐书籍《Netty in Action》(Norman Maurer)。本文不会对 Netty 技术做深入的阐述,有兴趣的同学也可以订阅我的专栏《Netty 核心源码解读》。

言归正传,构建基于 Netty 实现 TCP 网关的第一步,就是 Netty 版本的选型问题,当时调研了 3.x 的 jboss 和 4.x 的改进版本,包括 Mina 的技术,最终综合考虑选择了 Netty 4.x 的主流版本。其次在架构结构的设计上,由于 Netty 本身就是一个容器服务,这就与 HTTP 网关需要 Nginx + Tomcat 的部署架构有所不同,APP 客户端可以通过域名和端口直接访问到 Netty 服务,也基于此,通过不同域名对应各地域的 VIP,VIP 发布在 LVS,再由 LVS 将请求转发给后端的 HAProxy,最后由 HAProxy 把请求转发给后端的 TCP Netty 网关上,部署结构如下图:

期间,遇到逐多技术上的小问题,尤为烦扰的就是长连接的保持问题,因为网络问题导致 TCP 长连接很容易闪断,这里首先跟网络部协同优化了很多细节,包括对 keepalive 参数设置和 gzip 的数据压缩的调优等,其次是在 TCP 网关的 Session 设计和弱网闪断重连等多个技术细节点上做了上的创新,最终实现了百万级 TCP 长连接的稳定服务。

2。Protobuf 格式

当然,提供一个稳定的 TCP 长连接服务更离不开对通信协议的设计考量,之前 HTTP 网关是基于 JSON 进行数据传输的,JSON 是 key-value 的键值对通信协议,生成报文的会很大,所以传输性能会有所影响。考虑到报文的传输性能,构建 TCP 网关的通信协议选型最后采用了 Protocol Buffer,一是 Protobuf 协议天然支持 Java、Objective-C 和 C++ 等语言,做到了语言无关、平台无关;二是 Protobuf 协议数据压缩比很高,通常一个整型要占8比特位,通过 Protobuf 可以压缩到2比特位进行通信传输,提升数据传输效率。

因为目前 API 网关已经支持了泛化调用,泛化可以理解为通过配置和协议转化直接调用后端服务接口,所以,此时也就不需要每次有新需求,都要在网关增加 Protocol Buffer 对象定义新接口。数据传输的本质都是字节流的序列化和反序列化,所以 APP 的数据流可以以二进制流的方式在 TCP 网关直接反序列化为后端服务的接口对象,完成整条通信链路 API 服务的请求调度。

3。业务线程池

接下来,我们来谈谈业务线程池,一个疑问:为什么要有业务线程池?其实,我在初期构建 TCP 网关也是没有业务线程池的,直到一次事件后才加了单独的业务线程池。其实,逻辑很简单,我们知道通过 Netty 的 ChannlRead 方法就能方便的获取到通信的入站(Inbound)和出站(Outbound)数据,如果在 ChannelRead 方法里直接调用后端服务请求,就有可能由于后端服务响应 RT 高而阻塞住 Netty 的 IO 线程池组。为了说清楚这其中的原由,我先简单的介绍下 Netty 的线程池模型。

Netty 是 Reactor 模式的一个实现,Reactor 是一种经典的线程模型,Reactor 模型分为单线程模型、多线程模型和主从多线程模型,三种常用模式。

Reactor 单线程模型

This chapter requires login to view full content. You are viewing a preview.

Login to View Full Content

Course Curriculum

1

2013 京麦平台

负责京麦(jm.jd.com)从0到1的平台化转型,打造面向商家一站式工作台,为京东商家提供移动和桌面端的操作业务;负责京麦服务端研发,构建高可用的 TCP 网关,演变成为支撑数百万级长连接的架构平台。
3

2018 京东服务市场

负责京东服务市场研发(fw.jd.com)从1到2的发展,完成京东服务市场和京麦插件市场的系统融合,进行 SOA 微服务化改造,演变成为支持千万级订单、亿交易额的交易服务平台;负责平台国际化改造,支持海外站点快速部署。
4

2019 阿里云通信

负责阿里云(aliyun.com)云通信短信全球对客、对供网关从1到2的架构演进和研发落地,聚焦云通信规模化、平台化、全球化的发展方向,深度参与和推动生态平台化项目、CMPP磐石项目、国际站稳定性专项等。
5

2025 阿里云飞天实验室

负责 Qwen Chat(chat.qwen.ai)服务架构的演进,支撑百万级 DAU 的用户流量,保障聊天会话的安全性与系统稳定性;提供 Agent 与 LLM 能力,并全面兼容 Qwen 全系列开源模型,为用户提供多样化的智能服务。