写点什么

构建通用 WebSocket 推送网关的设计与实践

2021 年 3 月 07 日

构建通用WebSocket推送网关的设计与实践

HTTP 协议是一种无状态的、基于 TCP 的请求/响应模式的协议,请求只能由客户端发起、服务端进行响应。在大多数场景,这种请求/响应的 Pull 模式已经可以满足需求。但在某些情形,例如消息推送、通知等应用场景,需要实时将数据同步到客户端,这就要求服务端支持主动 Push 数据。


服务端推送技术历史悠久,经历了短轮询、长轮询的发展,一定程度上能够解决问题,但也存在着不足,例如时效性、资源浪费等。HTML5 标准带来的 WebSocket 规范基本结束了这一局面,成为目前服务端推送技术的主流方案。


在系统中集成 WebSocket 十分简单,相关讨论与资料很丰富。但如何实现一个通用的 WebSocket 推送网关尚未有成熟的方案。目前的云服务厂商主要关注 iOS 和安卓等移动端推送,也缺少对 WebSocket 的支持。本文介绍了我们基于 Netty 实现 WebSocket 长连接网关时的一些思考和经验。

1 爱奇艺号 WebSocket 使用现状

爱奇艺号是爱奇艺内容创作、分发和变现的平台,涵盖自媒体、网大、网剧、儿童、知识、纪录片等多个业务,是爱奇艺内容生态的重要组成。爱奇艺号作为前台系统,对用户体验有较高要求,直接影响着创作者的创作热情。目前,爱奇艺号有多个业务场景中用到了 WebSocket 推送技术,包括:


  • 用户评论。实时的将评论消息推送到浏览器。

  • 实名认证。合同签署前需要对用户进行实名认证,用户扫描二维码后进入第三方的认证页面,认证完成后异步通知浏览器认证的状态。

  • 活体识别。类似实名认证,当活体识别完成后,异步将结果通知浏览器。


在实际的业务开发中,我们发现,WebSocket 推送技术在使用中存在以下问题:首先,WebSocket 技术栈不统一,既有基于 Netty 实现的,也有基于 Web 容器实现的,给开发和维护带来困难;其次,WebSocket 实现分散在在各个工程中,与业务系统强耦合,如果有其他业务需要集成 WebSocket,面临着重复开发的窘境,浪费成本、效率低下;第三,WebSocket 是有状态协议的,客户端连接服务器时只和集群中一个节点连接,数据传输过程中也只与这一节点通信。


因此,WebSocket 集群需要解决会话共享的问题。如果只采用单节点部署,虽然可以避免这一问题,但无法水平扩展支撑更高负载,有单点的风险;最后,缺乏监控与报警,虽然可以通过 Linux 的 Socket 连接数大致评估 WebSocket 长连接数,但数字并不准确,也无法得知用户数等具有业务含义的指标数据;无法与现有的微服务监控整合,实现统一监控和报警。

2 长连接网关的设计与实现

为了解决以上问题,我们实现了统一的 WebSocket 长连接网关,具备如下特点:


1. 集中实现长连接管理和推送能力。统一技术栈,将长连接作为基础能力沉淀,便于功能迭代和升级维护。


2. 与业务解耦。将业务逻辑与长连接通信分离,使业务系统不再关心通信细节,也避免了重复开发,浪费研发成本。


3. 使用简单。提供 HTTP 推送通道,方便各种开发语言的接入。业务系统只需要简单的调用,就可以实现数据推送,提升研发效率。


4. 分布式架构。实现多节点的集群,支持水平扩展应对业务增长带来的挑战;节点宕机不影响服务整体可用性,保证高可靠。


5. 多端消息同步。允许用户使用多个浏览器或标签页同时登陆在线,保证消息同步发送。


6. 多维度监控与报警。自定义监控指标与现有微服务监控系统打通,出现问题时可及时报警,保证服务的稳定性。

2.1. 技术选型

在众多的 WebSocket 实现中,从性能、扩展性、社区支持等方面考虑,最终选择了 Netty。Netty 是一个高性能、事件驱动、异步非阻塞的网络通信框架,在许多知名的开源软件中被广泛使用。


WebSocket 是有状态的,无法像直接 HTTP 以集群方式实现负载均衡,长连接建立后即与服务端某个节点保持着会话,因此集群下想要得知会话属于哪个节点,有两种方案,一种是使用类似微服务的注册中心来维护全局的会话映射关系,一种是使用事件广播由各节点自行判断是否持有会话,两种方案对比如表 1 所示。

方案

优点

缺点

注册中心

会话映射关系清晰,集群规模较大时更合适

实现复杂,强依赖注册中心,有额外运维成本

事件广播

实现简单更加轻量

节点较多时,所有节点均被广播,资源浪费

表 1:WebSocket 集群方案


综合考虑实现成本与集群规模,选择了轻量级的事件广播方案。实现广播可以选择基于 RocketMQ 的消息广播、基于 Redis 的 Publish/Subscribe、基于 ZooKeeper 的通知等方案,其优缺点对比如表 2 所示。从吞吐量、实时性、持久化、实现难易等方面考虑,最终选择了 RocketMQ。


方案

优点

缺点

基于RocketMQ

吞吐量高、高可用、保证可靠

实时性不如Redis

基于Redis

实时性高、实现简单

不保证可靠

基于ZooKeeper

实现简单

写入性能较差,不适合频繁写入场景

表 2:广播的实现方案对比

2.2. 系统架构

网关的整体架构如图 1 所示。



图 1:WebSocket 长连接网关架构

网关的整体流程如下:


1. 客户端与网关任一节点握手建立起长连接,节点将其加入到内存维护的长连接队列。客户端定时向服务端发送心跳消息,如果超过设定的时间仍没有收到心跳,则认为客户端与服务端的长连接已断开,服务端会关闭连接,清理内存中的会话。

2. 当业务系统需要向客户端推送数据时,通过网关提供的 HTTP 接口将数据发向网关。

3. 网关在接收到推送请求后,将消息写入 RocketMQ。

4. 网关作为消费者,以广播模式消费消息,所有节点都会接收到消息。

5. 节点接收到消息后判断推送的消息目标是否在自己内存中维护的长连接队列里,如果存在则通过长连接推送数据,否则直接忽略。


网关以多节点方式构成集群,每节点负责一部分长连接,可实现负载均衡,当面对海量连接时,也可以通过增加节点的方式分担压力,实现水平扩展。同时,当节点出现宕机时,客户端会尝试重新与其他节点握手建立长连接,保证服务整体的可用性。

2.3. 会话管理

长连接建立起来后,会话维护在各节点的内存中。SessionManager 组件负责管理会话,内部使用了哈希表维护了 UID 与 UserSession 的关系;UserSession 代表用户维度的会话,一个用户可能会同时建立多个长连接,因此 UserSession 内部同样使用了一个哈希表维护 Channel 与 ChannelSession 的关系。为了避免用户无限制的创建长连接,UserSession 在内部的 ChannelSession 超过一定数量后,会将最早建立的 ChannelSession 关闭,减少服务器资源占用。SessionManager、UserSession、ChannelSession 的关系如图 2 所示。



图 2:SessionManager 组件

2.4. 监控与报警

为了了解集群建立了多少长连接、包含了多少用户,网关提供了基本的监控与报警能力。网关接入了 Micrometer,将连接数与用户数作为自定义指标暴露,供 Prometheus 进行采集,实现了与现有的微服务监控系统打通。在 Grafana 中方便地查看连接数、用户数、JVM、CPU、内存等指标数据,了解网关当前的服务能力与压力。报警规则也可以在 Grafana 中配置,当数据异常时触发奇信(内部报警平台)报警。

2.5. 性能压测

压测选择两台配置为 4 核 16G 的虚拟机,分别作为服务器和客户端。压测时选择为网关开放了 20 个端口,同时建立 20 个客户端,每个客户端使用一个服务端端口建立起 5 万连接,可以同时创建百万个连接。连接数与内存使用情况如图 3 所示。



图 3:百万级连接

给百万个长连接同时发送一条消息,采用单线程发送,服务器发送完成的平均耗时在 10s 左右,如图 4 所示。


图 4:服务器推送耗时

一般同一用户同时建立的长连接都在个位数。以 10 个长连接为例,在并发数 600、持续时间 120s 条件下压测,推送接口的 TPS 大约在 1600+,如图 5 所示。



图 5 :长连接 10、并发 600、持续时间 120s 的压测数据

当前的性能指标已满足爱奇艺号的实际业务场景,可支持未来的业务增长。

3 业务案例

为了更生动的说明优化效果,文章最后,我们也以封面图添加滤镜效果为例,介绍一个爱奇艺号使用 WebSocket 网关的案例。


爱奇艺号自媒体发表视频时,可选择为封面图添加滤镜效果,引导用户提供提供更优质的封面。当用户选择一个封面图后,会提交异步的后台处理任务。当异步任务处理完成后,通过 WebSocket 将不同滤镜效果处理后的图片返回给浏览器,业务场景如图 6 所示。



图 6:爱奇艺号视频封面图滤镜


从研发效率方面考虑,如果在业务系统中集成 WebSocket,至少需要 1-2 天的开发时间;如果直接使用网关的推送能力,只需要简单的接口调用就实现了数据推送,开发时间降低到分钟级别,研发效率大大提高。从运维成本方面考虑,业务系统不再含有与业务逻辑无关的通信细节,代码的可维护性更强,系统架构变得更加简单,运维成本大大降低。

4 写在最后

WebSocket 是目前实现服务端推送的主流技术,恰当使用能够有效提供系统响应能力,提升用户体验。通过 WebSocket 长连接网关可以快速为系统增加数据推送能力,有效减少运维成本,提高开发效率。


长连接网关的价值在于它封装了 WebSocket 通信细节,与业务系统解耦,使得长连接网关与业务系统可独立优化迭代,避免重复开发,便于开发与维护。其次,网关提供了简单易用的 HTTP 推送通道,支持多种开发语言接入,便于系统集成和使用。另外,网关采用了分布式架构,可以实现服务的水平扩容、负载均衡与高可用。最后,网关集成了监控与报警,当系统异常时能及时预警,保证服务的健康和稳定。

 

目前,WebSocket 长连接网关已在爱奇艺号图片滤镜结果通知、MCN 电子签章等多个业务场景中得到应用。未来还有许多方面需要探索,例如消息的重发与 ACK、WebSocket 二进制数据的支持、多租户的支持等。我们也会不断优化、丰富功能,带给开发者更好的使用体验。


本文转载自:爱奇艺技术产品团队(ID:iQIYI-TP)

原文链接:构建通用WebSocket推送网关的设计与实践

2021 年 3 月 07 日 08:001132

评论

发布
暂无评论
发现更多内容

自研ARM芯片,亲手拆掉Wintel联盟,微软这次是认真的吗?

脑极体

专家:区块链底层技术创新是关键

CECBC区块链专委会

区块链

如何给团队制定合理的季度绩效?

Alan

团队管理 绩效 七日更 28天写作

架构师训练营 1 期第 8 周:性能优化(二)- 作业

灵霄

极客大学架构师训练营

为移动应用产业开辟出海新航路,华为应用市场是如何“破冰”的?

脑极体

也谈“中年焦虑”

程序员架构进阶

方法论 职业规划 中年危机

简明设计模式—创建型

2970

golang 设计模式

IDEA插件:多线程文件下载插件开发

Silently9527

Java 多线程 idea插件 文件传输

架构师训练营 1 期第 12 周:数据应用(一) - 作业

灵霄

极客大学架构师训练营

Devil Fruit恶魔果实APP系统软件开发

开發I852946OIIO

系统开发

甲方日常 76

句子

工作 随笔杂谈 日常

重磅盘点!2020年区块链行业十件大事

CECBC区块链专委会

区块链

工具词典:Inner Peace

lidaobing

随机漫步的傻瓜 28天写作

云原生时代跨语言间微服务的打法

Damon

Kubernetes 微服务框架

【Java并发编程】阿里最喜欢问的几道线程池的面试题?

java金融

Java 面试题 线程池

【Java并发编程】面试必备之线程池

java金融

线程池

冰河又一MySQL力作出版(文末送书)!!

冰河

MySQL 高可用 高并发 高性能 MySQL架构

架构师训练营第五周”技术选型一“总结

随秋

极客大学架构师训练营

架构师训练营 1 期第 11 周:安全稳定 - 作业

灵霄

极客大学架构师训练营

架构师训练营 1 期第 13 周:数据应用(二) - 作业

灵霄

极客大学架构师训练营

重学JS | 找出数组中出现次数最多元素的4种算法

梁龙先森

前端 编程语言

面试官:我问的是Java内存模型,你回答堆栈方法区干嘛?

Java鱼仔

Java 程序员 JMM 多线程 并发

架构师训练营 1 期第 9 周:性能优化(三)

灵霄

极客大学架构师训练营

生产环境全链路压测建设历程 22:FAQ 1&2

数列科技杨德华

全链路压测 七日更

Go 并发基础

Damon

go Go web

突破2.8万美元关口,比特币为何“疯涨”? ​

CECBC区块链专委会

比特币 比特币数字货币

架构师训练营第五周”技术选型一“作业

随秋

极客大学架构师训练营

重学JS | 数组去重的7种算法

梁龙先森

前端 编程语言

像用户一样测试:不妨犯傻

QualityFocus

软件测试 体验 可用性 用户体验

架构师训练营 1 期第 10 周:模块分解 - 作业

灵霄

极客大学架构师训练营

一代版本一代神:利用Docker在Win10系统极速体验Django3.1真实异步(Async)任务

刘悦的技术博客

django python3.x 异步 异步任务

2021年全国大学生计算机系统能力大赛操作系统设计赛 技术报告会

2021年全国大学生计算机系统能力大赛操作系统设计赛 技术报告会

构建通用WebSocket推送网关的设计与实践-InfoQ