消息 Mesh
作为蚂蚁集团向下一代云原生架构演进的核心基础设施,Service Mesh 在 2019 年得到了大规模的应用与落地。截止目前,蚂蚁集团的 Service Mesh 数据平面 MOSN 已接入应用数百个,接入容器数量达数十万。同时,在双十一大促中,Service Mesh 的表现也十分亮眼,RPC 峰值 QPS 达到了几千万,消息峰值 TPS 达到了几百万,且引入 Service Mesh 后的平均 RT 增长幅度控制在 0.2 ms 以内。
本文将从以下几个方面对消息 Mesh 进行解读:
消息 Mesh 介绍:解答消息 Mesh 在整个 Service Mesh 中的地位是什么,它又能为业务带来哪些价值。
消息 Mesh 的价值:介绍消息 Mesh 所能带来的价值和收益。
消息 Mesh 化改造:结合蚂蚁集团消息中间件团队关于 Mesh 化的实践与思考,阐述如何在消息领域进行 Mesh 化改造。
消息 Mesh 的挑战:消息 Mesh 在蚂蚁集团内部大规模落地过程中遇到的问题与挑战,以及对应的解决方案。
消息 Mesh 流量调度:消息流量精细化调度上的思考和在 Mesh 上的实现与落地。
消息 Mesh 简介
Service Mesh 作为云原生场景下微服务架构的基础设施(轻量级的网络代理),正受到越来越多的关注。Service Mesh 不仅负责在微服务架构的复杂拓扑中可靠地传递请求,也将限流、熔断、监控、链路追踪、服务发现、负载均衡、异常处理等与业务逻辑无关的流量控制或服务治理行为下沉,让应用程序能更好地关注自身业务逻辑。
微服务架构中的通信模式实际上是多种多样的,包含:
同步的请求调用。
异步的消息或事件驱动。
流行的 Service Mesh 实现(Istio、Linkerd、Consul Connect 等),仍局限在对微服务中同步请求调用的关注,却无法管理和追踪异步消息流量。
消息 Mesh 是对这一块的重要补充,通过将消息 Mesh 有机地融合到 Service Mesh 中,可以帮助 Service Mesh 实现对所有微服务流量的管控和追踪,从而进一步完善其架构目标。
消息 Mesh 的价值
在传统的消息中间件领域,我们更关注的核心指标为:
服务端的性能
服务可用性
数据可靠性等
有些能力与业务应用密切相关却表现不佳,主要包括:
消息的流量控制:限流、熔断、灰度、着色、分组等。
消息的服务治理:消息量级与消息应用拓扑等。
消息链路的追踪:消息轨迹
造成这个局面的原因包括:
传统模式下上述能力的提供和优化都涉及客户端的改造与大规模升级,整个过程常常比较漫长,难以快速根据有效反馈不断优化。
缺乏一个统一的架构指导思想,混乱无序地向客户端叠加相关功能只会让消息客户端变得越来越臃肿和难以维护,也变相增加了业务系统的接入、调试和排查问题的成本。
消息 Mesh 作为 Service Mesh 的补充,能显著带来如下价值和收益:
快速升级:通过将与业务逻辑无关的一些核心能力下沉到 Sidecar 中,使这些能力的单独快速迭代与升级成为可能。
流量控制:可以向 Sidecar 中集成各种流量控制策略,例如可根据消息类型、消息数量、消息大小等多种参数来控制消息的发送和消费速率。
流量调度:通过打通 Sidecar 节点之间的通信链路,可以利用 Sidecar 的流量转发来实现任意精度的消息流量调度,帮助基于事件驱动的微服务应用进行多版本流量管理、流量着色、分组路由、细粒度的流量灰度与A/B策略等。
消息验证:消息验证在基于事件驱动的微服务架构中正变得越来越重要,通过将这部分能力下沉到 Sidecar,不仅可以让业务系统无缝集成消息验证能力,也可以让 Sidecar 通过 Schema 理解消息内容,并进一步具备恶意内容识别等安全管控能力。
可观测性:由于所有的消息流量都必须通过 Sidecar,因此可以为 Sidecar 上的消息流量按需增加 Trace 日志、Metrics 采集、消息轨迹数据采集等能力,并借此进一步丰富消息服务的治理能力。
消息 Mesh 化改造
在蚂蚁集团内部,Msgbroker 基于推送模型提供高可靠、高实时、事务消息、Header 订阅等特性,帮助核心链路进行异步解耦,提升业务的可扩展能力,并先后伴随蚂蚁集团众多核心系统一起经历了分布式改造、单元化改造与弹性改造,目前主要承载蚂蚁内部交易、账务、会员、消费记录等核心在线业务的异步消息流量。
由于 Service Mesh 的推进目标也是优先覆盖交易支付等核心链路,为在线业务赋能,因此我们优先选择对 Msgbroker 系统进行 Mesh 化改造。
下文将以 Msgbroker 为例,重点剖析 Mesh 化后,其在整体架构和核心交互流程上的变化,为消息领域的 Mesh 化改造提供参考。
整体架构
消息 Mesh 化后的整体架构,如下图所示:
与原有的消息架构相比,主要的变化有:
客户端不再与服务端直连,而是通过 Sidecar 进行请求的中转。对客户端而言,Sidecar 实际上是它唯一能感知到的消息服务端;对服务端而言,Sidecar 则扮演着客户端的角色。
所有 Sidecar 都会与控制平面交互,接收服务端地址列表、流量管控和调度配置、运行时动态配置等的下发,从而使数据平面具备限流、熔断、异常重试、服务发现、负载均衡、精细化流量调度等能力。
核心交互流程
当 Sidecar 代理了消息客户端的所有请求后,一旦 Sidecar 完成消息服务的发现与服务端/客户端路由数据的缓存,无论是客户端的发消息请求还是服务端的推消息请求,都能由 Sidecar 进行正确的代理转发,而这一切的关键,则是 Sidecar 与消息客户端协同完成一系列的初始化操作。
消息 Mesh 化后具体的初始化流程,如下图所示:
与原有的初始化流程相对比,主要有如下不同:
消息服务端的地址处理:
Mesh 化前:消息客户端直接向 SOFARegistry 订阅消息服务端的地址。
Mesh 化后:
消息客户端将所有消息元数据(包含业务应用声明的消息 Topic、发送/订阅组 GroupId 等关键信息)通过 HTTP 请求上报给 MOSN。
由 MOSN 进行元数据的持久化(用于 MOSN 异常 Crash 后的恢复)以及消息服务端地址的订阅和处理。
客户端收到 MOSN 注册请求响应的处理:
客户端会主动与 MOSN 建立连接,并将与该连接相关的 Group 集合信息通过控制指令发送给 MOSN。由于客户端与 MOSN 可能存在多个连接,且不同连接上的 Group 集合可以不同,而 MOSN 与同一个消息服务端只维持一个连接,因此控制指令无法向消息数据一样直接进行转发,而是需要汇总计算所有 GroupId 集合后,再统一广播给消息服务端集群。
由于上述计算逻辑十分复杂,需要包含过滤和聚合,且存在动态和并发行为,一旦因计算错误则会严重影响到消息投递的正确性。因此,当前 MOSN 绕过了该指令的代理,只利用客户端的控制指令进行相关数据的校验,以及更新客户端连接的映射信息(用于 MOSN 的客户端连接选择),选择依赖消息客户端的改造,引入上述 HTTP 注册请求,来构造全量控制指令。
消息 Mesh 的挑战
消息中间件最关键的挑战在于如何在洪峰流量下依然保障消息服务的高可靠与高实时。而在消息 Mesh 化的大规模实施过程中,还需要考虑数十万的容器节点对数据面整体稳定性和控制面性能带来的巨大挑战。这些都依赖于完善的 Sidecar 运维体系,包括 Sidecar 的资源分配策略,以及 Sidecar 独立变更升级的策略。
资源管理
当 Service Mesh 的实体 MOSN 作为 Sidecar 与业务容器部署在一起时,就不再像消息服务端一样,只需要关心自身的资源消耗,而是必须精细化其 CPU、内存等资源的分配,才能达到与应用最优的协同合作方式。
Sidecar 的精细化资源管理经历了下述多个阶段:
独立分配
通过超卖与业务容器共享
细粒度的 CPU 资源分配策略
内存 OOM 策略调整等
Service Mesh 将原有的与业务无关的逻辑下沉到 Sidecar,其占用的资源实际是原来业务容器会使用的资源,基于这一假设,在零新增成本的情况下,Service Mesh 平稳支撑了数十万规模级别的 Sidecar 容器分配。
平滑升级
为了达到 Sidecar 这一类基础设施的变更升级对业务完全无感知的目的,就需要使 MOSN 具备平滑升级的能力。
平滑升级的隐含前提条件:单条连接上的请求必须是单向的。
平滑升级的过程为:新的 MOSN 首先会被注入,并通过共享卷的 UnixSocket 检查是否存在老的 MOSN。若存在,则利用内核 Socket 的迁移实现老 MOSN 的连接全部迁移给新 MOSN,最终让老 MOSN 优雅退出,从而实现 MOSN 在整个升级和发布过程中对业务无任何打扰。
平滑升级过程示意图
关于 MOSN 本身平滑升级更多的内容,请参考 服务网格最佳实践之核心篇。
平滑升级方案与 RPC
RPC 可以直接使用上述平滑升级方案,理由如下:
其单条连接的角色是固定的,只能是服务端连接或客户端连接。
对一次请求的代理过程也是固定的,总是从服务端连接上收到一个请求,再从客户端连接将请求转发出去。
平滑升级方案与 MsgBroker
在消息场景特别是 Msgbroker 场景下,MOSN 上的连接请求实际上是双向的:
客户端会使用该连接进行消息的发送。
服务端也会利用该连接将消息主动推送给 MOSN。
这会给连接迁移带来新的问题和挑战:
在连接迁移的过程中,如果消息客户端已处理完经过 MOSN 转发的服务端投递消息请求,但是还未回复响应,此时若把连接迁移到新的 MOSN,则新的 MOSN 将收到上述响应,但由于新 MOSN 缺失上下文,无法将该响应返回给正确的消息服务端。
在连接迁移完成,但老 MOSN 还未优雅退出期间,由于两个 MOSN 与消息服务端都存在连接,两者都会收到服务端发送的投递消息请求,但因两个 MOSN 与服务端连接的状态各自独立,可能会使客户端收到的请求 ID 相冲突。
解决上述问题的思路其实很简单,即在平滑升级的过程中,禁止服务端向老 MOSN 发送投递消息请求,以保证即使在消息场景整个平滑升级过程中,所有连接仍然是单工通信的。对平滑升级流程的具体改动,说明如下:
老 MOSN 平滑升级指令后,会立即向所有的消息服务端发送禁止再接收消息的控制指令。
新 MOSN 感知老 MOSN 完成前置操作,开始进行原有的平滑升级流程,进行初始化和存量连接迁移。
新 MOSN 完成存量连接迁移后,向所有的消息服务端发送接收消息的控制指令,开始正常的消息订阅。
消息 Mesh 流量调度
消息 Mesh 的流量调度,示例如下:
流量调度流程说明如下:
控制平面会将与流量调度相关的规则下发至 MOSN,规则主要包含该应用下所有容器节点的 IP 地址与流量权重,这是能够进行精细化流量调度的前提。
当 MOSN 收到消息投递请求时,会判断请求的来源:
若来自于其他 MOSN 节点,则会直接将该请求转发给客户端,避免消息投递请求的循环转发。
若来自于消息服务端,则 MOSN 会根据自身的流量权重来决定下一步的路由:
若自身的流量权重是 100%,会同样将该请求转发给客户端。
若自身权重小于 100%,则会按照配置的权重,将剩余请求均匀转发给其他流量权重为 100% 的 MOSN 节点。
与 RPC 的点对点通信方式不同,无论是消息发送端还是订阅端,都只与消息服务端通信。这意味着:
即使进行了消息 Mesh 化改造后,MOSN 也只与消息服务端通信。
同一个应用的 MOSN 节点之间是不存在消息连接的。
为了实现 MOSN 之间的消息流量转发,需要内置实现一个与业务应用进程同生命周期的消息转发服务,由同应用内的所有其他 MOSN 节点订阅,并在需要转发时调用。
消息 Mesh 经过蚂蚁消息中间件团队大半年的打磨和沉淀,已经迈出了坚实的一大步:在开源社区迟迟未在消息 Mesh 上取得实质性进展时,蚂蚁团队已经为蚂蚁内部主流消息中间件打通了数据平面。
同时,依赖消息的精细化流量调度,预期可以发掘出更大的业务价值,包括:
基于事件驱动的 Serverless 化应用多版本流量管理
流量着色
分组路由
细粒度的流量灰度与 A/B 策略
未来,蚂蚁将会持续加大对消息 Mesh 的投入,为消息 Mesh 支持更多的消息协议,赋予更多开箱即用的消息流量管控和治理能力,并进一步结合 Knative 探索消息精细化流量调度在 Serverless 下的应用场景。