Nacos 通信通道
柳遵飞(翼严) Nacos commiter
Nacos长链接
一.现状背景
Nacos 1.X 版本 Config/Naming 模块各自的推送通道都是按照自己的设计模型来实现的。
产品 | 推送模型 | 数据一致性 | 痛点 | 说明 |
---|---|---|---|---|
Nacos Config | 异步 Servlet | 基于MD5比对一致性 | http短连接,30秒定期创建销毁连接,GC压力大 | md5值计算也有一定开销,在可接受范围内 |
Nacos Naming | HTTP/UDP | UDP 推送 + 补偿查询 | 丢包,云架构下无法反向推送 |
配置和服务器模块的数据推送通道不统一,http 短连接性能压力巨大,未来 Nacos 需要构建能够同时支持配置以及服务的长链接通道,以标准的通信模型重构推送通道。
二.场景分析
1.配置
配置对连接的场景诉求分析
- SDK 和 Server 之间
- 客户端 SDK 需要感知服务节点列表,并按照某种策略选择其中一个节点进行连接;底层连接断开时,需要进行切换Server进行重连。
- 客户端基于当前可用的长链接进行配置的查询,发布,删除,监听,取消监听等配置领域的 RPC 语意接口通信。
- 感知配置变更消息,需要将配置变更消息通知推送当前监听的客户端;网络不稳定时,客户端接收失败,需要支持重推,并告警。
- 感知客户端连接断开事件,将连接注销,并且清空连接对应的上下文,比如监听信息上下文清理。
- Server 之间通信
- 单个 Server 需要获取到集群的所有 Server 间的列表,并且为每一个 Server 创建独立的长链接;连接断开时,需要进行重连,服务端列表发生变更时,需要创建新节点的长链接,销毁下线的节点长链接
- Server 间需要进行数据同步,包括配置变更信息同步,当前连接数信息,系统负载信息同步,负载调节信息同步等。
2.服务
- SDK 和 Server 之间
- 客户端 SDK 需要感知服务节点列表,并按照某种策略选择其中一个节点进行连接;底层连接断开时,需要切换 Server 进行重连。
- 客户端基于当前可用的长链接进行配置的查询,注册,注销,订阅,取消订阅等服务发现领域的RPC 语意接口通信。
- 感知服务变更,有服务数据发生变更,服务端需要推送新数据到客户端;需要有推送 ack,方便服务端进行 metrics 和重推判定等。
- 感知客户端连接断开事件,将连接注销,并且清空连接对应的上下文,比如该客户端连接注册的服务和订阅的服务。
- Server 之间通信
- 服务端之间需要通过长连接感知对端存活状态,需要通过长连接汇报服务状态(同步 RPC 能力)
- 服务端之间进行 AP Distro 数据同步,需要异步 RPC 带 ack 能力。
三. 长链接核心诉求
1.功能性诉求
客户端
- 连接生命周期实时感知能力,包括连接建立,连接断开事件
- 客户端调用服务端支持同步阻塞,异步future,异步 callback 三种模式
- 底层连接自动切换能力
- 响应服务端连接重置消息进行连接切换
- 选址/服务发现
服务端
- 连接生命周期实时感知能力,包括连接建立,连接断开事件
- 服务端往客户端主动进行数据推送,需要客户端进行 Ack 返回以支持可靠推送,并且需要进行失败重试
- 服务端主动推送负载调节能力
2.性能要求
性能方面,需要能够满足阿里的生产环境可用性要求,能够支持百万级的长链接规模及请求量和推送量,并且要保证足够稳定。
3.负载均衡
-
常见的负载均衡策略:随机,hash,轮询,权重,最小连接数,最快响应速度等
-
短连接和长链接负载均衡的异同:在短连接中,因为连接快速建立销毁,“随机,hash,轮询,权重”四种方式大致能够保持整体是均衡的,服务端重启也不会影响整体均衡,其中“最小连接数,最快响应速度”是有状态的算法,因为数据延时容易造成堆积效应;长连接因为建立连接后,如果没有异常情况出现,连接会一直保持,断连后需要重新选择一个新的服务节点,当出现服务节点发布重启后,最终连接会出现不均衡的情况出现,“随机,轮询,权重”的策略在客户端重连切换时可以使用,“最小连接数,最快响应速度”和短连接一样也会出现数据延时造成堆积效应。长连接和短连接的一个主要差别在于在整体连接稳定时,服务端需要一个rebalance的机制,将集群视角的连接数重新洗牌分配,趋向另外一种稳态
-
客户端随机+服务端柔性调整
核心的策略是客户端+服务端双向调节策略,客户端随机选择+服务端运行时柔性调整。
客户端随机
- 客户端在启动时获取服务列表,按照随机规则进行节点选择,逻辑比较简单,整体能够保持随机。
服务端柔性调整
- (当前实现版本)人工管控方案:集群视角的系统负载控制台,提供连接数,负载等视图(扩展新增连接数,负载,CPU 等信息,集群间 report 同步),实现人工调节每个 Server 节点的连接数,人工触发reblance,人工削峰填谷
- 提供集群视角的负载控制台:展示 总节点数量,总长链接数量,平均数量,系统负载信息
- 每个节点的地址,长链接数量,与平均数量的差值,正负值
- 对高于平均值的节点进行数量调控,设置数量上限(临时和持久化),并可指定服务节点进行切换
- (未来终态版本)自动化管控方案:基于每个 server 间连接数及负载自动计算节点合理连接数,自动触发reblance,自动削峰填谷。实现周期较长,比较依赖算法准确性。
3.连接生命周期
心跳保活机制
类型 | TCP | netty | mina | grpc | rsocket | tb remote | |
---|---|---|---|---|---|---|---|
心跳保活机制 | keepalive机制:通道无读写事件时,发送心跳包检测,可设置超时时间,间隔次数 | 1.设置TCP参数 2.自定义心跳IdeHandler,监听通道读写事件 | 1.自定义心跳,KeepAliveFilter | 1.自定义心跳,ping-pong包探测 | 1.自定义keep alive机制 | 基于mina,KeepAliveFilter | |
事件通知 | 正常关闭 | 有事件通知 | 有事件通知 | 有事件通知 | 有事件通知 | 有事件通知 | 有事件通知 |
断网异常 | keep alive机制,有事件通知 | tpc及自定义心跳,有事件通知 | 自定义心跳,有事件通知 | 自定义心跳,ping-pong包探测,无事件通知 | 1.自定义心跳,有事件通知 | 自定义心跳,有事件通知 |
参考:理解 TCP Keepalive: https://blog.csdn.net/chrisnotfound/article/details/80111559
grpc keepalive :https://blog.csdn.net/zhaominpro/article/details/103127023
netty 的心跳检测:https://www.cnblogs.com/rickiyang/p/12792120.html
我们需要什么
- 低成本快速感知:客户端需要在服务端不可用时尽快地切换到新的服务节点,降低不可用时间,并且能够感知底层连接切换事件,重置上下文;服务端需要在客户端断开连接时剔除客户端连接对应的上下文,包括配置监听,服务订阅上下文,并且处理客户端连接对应的实例上下线。
- 客户端正常重启:客户端主动关闭连接,服务端实时感知
- 服务端正常重启 : 服务端主动关闭连接,客户端实时感知
- 防抖:
- 网络短暂不可用: 客户端需要能接受短暂网络抖动,需要一定重试机制,防止集群抖动,超过阈值后需要自动切换server,但要防止请求风暴。
- 断网演练:断网场景下,以合理的频率进行重试,断网结束时可以快速重连恢复。
5.安全性
支持基础的鉴权,数据加密能力
6.低成本多语言实现
在客户端层面要尽可能多的支持多语言,至少要支持一个 Java 服务端连接通道,可以使用多个主流语言的客户端进行访问,并且要考虑各种语言实现的成本,双边交互上要考虑 thin sdk,降低多语言实现成本。
7.开源社区
文档,开源社区活跃度,使用用户数等,面向未来是否有足够的支持度。
四.长链接选型对比
grpc | WebSocket | tbremote | Rsocket | netty | mina | ||
---|---|---|---|---|---|---|---|
客户端通信 | sync | 支持 | 支持 | 支持 | 支持 | 无rpc语意 | 无rpc语意 |
future | 支持 | 不支持 | 支持 | 支持 | 无rpc语意 | 无rpc语意 | |
callback | 结合 guava实现 | 不支持 | 支持 | 支持 (依赖jdk 1.8+ completableFuture) | 无rpc语意 | 无rpc语意 | |
服务端推送 | sync | 无ack | 支持 | 支持 | 支持 | 无rpc语意 | 无rpc语意 |
future | 不支持 | 支持 | 支持 | 支持 | 无rpc语意 | 无rpc语意 | |
callback | 不支持 | 支持 | 支持 | 支持 | 无rpc语意 | 无rpc语意 | |
连接生命周期 | 客户端感知断连 | 无 (基于stream流 error complete事件可实现) | 支持 | 支持 | 支持 | 支持 | 支持 |
服务端感知断连 | 支持 (官方不建议使用) | 支持 | 支持 | 支持 | 支持 | 支持 | |
心跳保活 | 应用层自定义,ping-pong消息 | 应用层自定义,单byte ack | 自定义keepalive frame | TCP+ 自定义 | 自定义 keepalive filter | ||
性能 | tps | ||||||
安全性 | TLS | TLS | TLS | TLS | TLS | TLS | |
多语言支持 | JAVA | 支持 | 不支持 | 支持 | 支持 1.8+ 占比>93% | 支持 | 支持 |
GO | 支持 | 不支持 | 支持 | 支持 1.12+ 占比>93% | 无 | 无 | |
C++ | 支持 | 不支持 | 支持 | 支持 14+ 占比 >60% | 无 | 无 | |
C# | 支持 | 不支持 | 不支持 | 支持 | 无 | 无 | |
Node.js | 支持 | 不支持 | 支持 | 支持 | 无 | 无 | |
JS | 支持 | 支持 | 不支持 | 支持 | 无 | 无 | |
Ruby | 支持 | 不支持 | 不支持 | 支持 | 无 | 无 | |
Python | 支持 | 不支持 | 不支持 | 支持 3.6+ >96% | 无 | 无 | |
Kotlin | 支持 | 不支持 | 不支持 | 支持 | 无 | 无 | |
rust | 支持 1.39+ | ||||||
dart | 支持 2.6 | ||||||
其他 | Github Star/Issue | 最高go版本:11.9K/124 issue | 最高java版本:1.8/47 issue | ||||
备注 | 服务端推送Ack自建 | 阿里内部基于mina实现的私有协议 | rsocket私有协议,社区活跃度,用户数一般 | 需要自定义rpc协议 | 需要自定义rpc协议 |
在当前的备选框架中,从功能的契合度上,Rsocket 比较贴切我们的功能性诉求,性能上比 grpc 要强一些,开源社区的活跃度上相对grpc要逊色很多。
版本分布参考:https://blog.csdn.net/sinat_33224091/article/details/105002276
C++:https://www.jetbrains.com/zh-cn/lp/devecosystem-2020/cpp/
JAVA https://www.jetbrains.com/zh-cn/lp/devecosystem-2020/java/
GO:https://www.sohu.com/a/390826880_268033
五.基于长链接的一致性模型
1.配置一致性模型
i.sdk-server一致性
ii.server间一致性
*Server 间同步消息接收处理轻量级实现,重试失败时,监控告警
*断网:断网太久,重试任务队列爆满时,无剔除策略