2024年10月21日

基于 Apache RocketMQ 的 ApsaraMQ Serverless 架构升级
_本文整理于 2024 年云栖大会阿里云智能集团高级技术专家金吉祥(牟羽)带来的主题演讲《ApsaraMQ Serverless 能力再升级,事件驱动架构赋能 AI 应用》_ 云消息队列 ApsaraMQ 全系列产品 Serverless 化,支持按量付费、自适应弹性、跨可用区容灾,帮助客户降低使用和维护成本,专注业务创新。那 ApsaraMQ 是如何一步一步完成 Serverless 能力升级的?在智能化时代,我们的事件驱动架构又是如何拥抱 AI、赋能 AI 的? 本次分享将从以下四个方面展开: + 首先,回顾 ApsaraMQ Serverless 化的完整历程,即 ApsaraMQ 从阿里内部诞生、开源,到捐赠给 Apache 进行孵化,再到完成 Serverless 化升级的不断突破、与时俱进的过程。 + 然后,重点介绍 ApsaraMQ 的存算分离架构,这是全面 Serverless 化进程中不可或缺的前提。 + 接下来,会从技术层面重点解读近一年 ApsaraMQ Serverless 能力的演进升级,包括弹性能力的升级、基于 RDMA 进一步降低存储和计算层之间的 CPU 开销,以及对无感扩缩容的优化。 + 最后,介绍我们在面向 AI 原生应用的事件驱动架构上的探索,以及基于阿里云事件总线定向更新向量数据库,为 AI 应用融入实时最新数据的最佳实践。 ApsaraMQ Serverless 化历程 首先,我们来看 ApsaraMQ Serverless 化的整个发展历程。 + 2012 年,RocketMQ 在阿里巴巴内部诞生,并将代码在 Github 上开源; + 2016 年,云消息队列 RocketMQ 版开启商业化的同时,阿里云将 RocketMQ 捐赠给了 Apache,进入孵化期; + 2017 年,RocketMQ 以较短的时间孵化成为 Apache TLP,并快速迭代新功能特性,顺利发布 4.0 版本,支持了顺序、事务、定时等特殊类型的消息; + 2018 年,随着大数据、互联网技术的发展,数据量爆发式增长,云消息队列 Kafka 版商业化发布; + 2019 年,云消息队列 RabbitMQ 版、云消息队列 MQTT 版两款产品商业化发布,补齐了 ApsaraMQ 在 AMQP、MQTT 协议适配上的缺失; + 2021 年,经过一段时间的孵化,ApsaraMQ 家族中的另一款产品事件总线 EventBridge 开始公测,开始融合事件、消息、流处理; + 2023 年,ApsaraMQ 全系列产品依托存算分离架构,完成 Serverless 化升级,打造事件、消息、流一体的融合型消息处理平台; + 今年,我们专注提升核心技术能力,包括秒级弹性、无感发布、计算存储层之间的 RDMA 等,实现 Serverless 能力的进一步升级,并结合当下客户需求,通过事件驱动架构赋能 AI 原生应用。 存算分离架构全景 第二部分,我们来看 ApsaraMQ 存算分离架构的全景,这是 Serverless 化升级不可或缺的前序准备。 ApsaraMQ 存算分离架构全景:云原生时代的选择 ApsaraMQ 的存算分离架构,是云原生时代的选择。 + 从下往上看,这套架构是完全构建在云原生基础之上的,整个架构 K8s 化,充分利用 IaaS 层的资源弹性能力,同时也基于 OpenTelemetry 的标准实现了metrics、tracing 和 logging 的标准化。 + 再往上一层是基于阿里云飞天盘古构建的存储层,存储节点身份对等,Leaderless 化,副本数量灵活选择,可以在降低存储成本和保证消息可靠性之间实现较好的平衡。 + 再往上一层是在本次架构升级中独立抽出来的计算层,即无状态消息代理层,负责访问控制、模型抽象、协议适配、消息治理等。比较关键的点是,它是无状态的,可独立于存储层进行弹性。 + 在用户接入层,我们基于云原生的通信标准 gRPC 开发了一个全新的轻量级 SDK,与之前的富客户端形成了很好的互补。 “Proxy” 需要代理什么? 接下来我们重点看下这套架构里的核心点,即独立抽出来的 Proxy。 它是一层代理,那到底需要代理什么呢? 在之前的架构中,客户端与 Broker/NameServer 是直连模式,我们需要在中间插入一个代理层,由原先的二层变成三层。然后,将原先客户端侧部分业务逻辑下移,Broker、Namesrv 的部分业务逻辑上移,至中间的代理层,并始终坚持一个原则:往代理层迁移的能力必须是无状态的。 从这张图中,我们将原先客户端里面比较重的负载均衡、流量治理(小黑屋机制)以及 Push/Pull 的消费模型下移至了 Proxy,将原先 Broker 和 Namesrv 侧的访问控制(ACL)、客户端治理、消息路由等无状态能力上移至了 Proxy。然后在 Proxy 上进行模块化设计,抽象出访问控制、多协议适配、通用业务能力、流量治理以及通用的可观测性。 ApsaraMQ 存算分离的技术架构 接下来看 ApsaraMQ 存算分离的技术架构,在原先的架构基础上剥离出了无状态 Proxy 组件,承接客户端侧所有的请求和流量;将无状态 Broker,即消息存储引擎和共享存储层进行剥离,让存储引擎的职责更加聚焦和收敛的同时,充分发挥共享存储层的冷热数据分离、跨可用区容灾的核心优势。 这个架构中无状态 Proxy 承担的职责包括: 1. 多协议适配: 能够识别多种协议的请求,包括 remoting、gRPC 以及 Http 等协议,然后统一翻译成 Proxy 到 Broker、Namesrv 的 remoting 协议。 2. 流量治理、分发: Proxy 具备按照不同维度去识别客户端流量的能力,然后根据分发规则将客户端的流量导到后端正确的 Broker 集群上。 3. 通用业务能力扩展: 包含但不限于访问控制、消息的 Tracing 以及可观测性等。 4. Topic 路由代理: Proxy 还代理了 Namesrv 的 TopicRoute 功能,客户端可以向 Proxy 问询某个 topic 的详细路由信息。 5. 消费模型升级: 使用 Pop 模式来避免单客户端消费卡住导致消息堆积的历史问题。 无状态 Broker,即消息存储引擎的职责更加的聚焦和收敛,包括: 1. 核心存储能力的迭代: 专注消息存储能力的演进,包括消息缓存、索引的构建、消费状态的维护以及高可用的切换等。 2. 存储模型的抽象: 负责冷热存储的模型统一抽象。 共享存储为无状态 Broker 交付了核心的消息读写的能力,包括: 1. 热存储 EBS: 热存储 EBS,提供高性能、高可用存储能力。 2. 冷存储 OSS: 将冷数据卸载到对象存储 OSS,获取无限低成本存储空间。 3. 跨可用区容灾: 基于 OSS 的同城冗余、Regional EBS 的核心能力构建跨可用区容灾,交付一写多读的消息存储能力。 基于云存储的存算分离架构,兼顾消息可靠性和成本 存算分离架构中的存储层是完全构建在阿里云飞天盘古存储体系之上的。基于这套架构,我们能够灵活控制消息的副本数量,在保证消息可靠性和降低存储成本之间做到一个比较好的平衡。 左图是存算分离存储架构中上传和读取的流程。可以看到,我们是在 CommitLog 异步构建 consumeQueue 和 Index 的过程中额外添加了按照 topic 拆分上传到对象存储的过程;在读取过程中智能识别读取消息的存储 Level,然后进行定向化读取。 基于云存储的架构,我们提供了 ApsaraMQ 的核心能力,包括: 1. 超长时间定时消息: 超过一定时间的定时消息所在的时间轮会保存至对象存储上,快到期时时间轮再拉回到本地进行秒级精准定时。 2. 集群缩容自动接管: 消息数据实时同步到对象存储,对象存储的数据能够动态挂载到任意 Broker,实现彻底存算分离,Broker 的无状态化。 3. 按需设置 TTL 成本优化: 支持按需设置消息 TTL,不同重要程度的消息可设置不同的 TTL,满足消息保存时长需求的同时兼顾成本控制。 4. 冷热消息分离、分段预取: 热数据的读取命中本地存储,速度快;冷数据的读取命中远端存储,速率恒定且不会影响热数据的读取。 5. 动态调控云存储的比例: 动态调控 EBS 和 OSS 的比例,大比例的 EBS 能够具备更高的稳定性,应对 OSS 负载过高的背压,提升 EBS 作为 OSS 的前置容灾的能力;小比例的 EBS 则是可以最大化降低消息单位存储成本,让整体的存储成本趋同于 OSS 存储成本。 Serverless 能力再升级 有了存算分离架构的铺垫,ApsaraMQ 全系列产品 Serverless 化就更加顺畅了。接下来展开介绍 ApsaraMQ Serverless 能力的升级。 消息场景下的 Serverless 化 在消息场景下通常会有两个角色:消息服务的使用方和提供方。 在传统架构中通常是这样的流程:使用方会给提供方提需求:我要大促了,你保障下;提供方说给你部署 10 台够不够,使用方说好的;结果真到大促那一刻,真实流量比预估的要大很多倍,整个消息服务被击穿,硬生生挂了半小时。 在 Serverless 化改造前,仍需提前规划容量;但相比传统架构的提升点是,依托 IaaS 层的快速扩容能力,能够极大缩短扩容时间;再演进到当前的 Serverless 架构,我们期望消息服务提供方是能够非常淡定地应对大促。 Serverless 现在已经成为了一个趋势和云的发展方向。ApsaraMQ 全线产品实现弹性灵活的 Serverless 架构,既彰显了技术架构的先进性,也提升了客户的安全感。业务部门减少了容量评估的沟通成本,用多少付多少,更省成本;运维部门免去了容量规划,实现自动弹性扩缩,降低运维人员投入的同时,大大提升了资源的利用率。 Serverless 方案的 Why / What / How Serverless 化预期达到的收益是:省心——秒级弹性,免容量规划;省钱——用多少付多少;省力——少运维、免运维。 要解决的痛点是:用户使用容量模型比较难做精准预估;运维侧手动扩容,是一个非常耗时耗力的过程。 核心目标是: + 弹性要快: 特别是针对一些脉冲型的秒杀业务,需要具备秒级万 TPS 的弹性能力。 + 缩容无感: 应对 MQ 客户端与服务侧 TCP 长连接的特性,缩容、服务端发布时要无感。 + 成本要低: 极致的性能优化,才能进一步降低用户侧的单位 TPS 成本。 通过如下几个关键路径构建 ApsaraMQ Serverless 核心能力: + 多租、安全隔离、业务流量隔离: 是构建 Serverless 核心能力的基础。 + 物理预弹&逻辑弹性: 物理预弹和逻辑弹性结合的极致弹性方案,通过镜像加速、元数据批量创建、主动路由更新等核心技术加速物理弹性,结合逻辑弹性最终交付秒级万 TPS 的极致弹性能力。 + RDMA: 在存储和计算层之间引入 RDMA 技术,充分利用 RDMA 的零拷贝、内核旁路、CPU 卸载等特性进一步降低计算层与存储层之间的通信开销。 + 优雅上下线: 依托 gRPC 协议的 GOAWAY 以及 Remoting 协议的扩展实现 TCP 长连接的优雅上下线。 + 控制资源水位: 通过智能化的集群流量调度以及平滑 Topic 迁移方案,进一步控制单个集群的资源水位在一个合理的值。 这套 Serverless 方案可以同时满足如下几种场景: + 第一种是平稳型:流量上升到一定水位后会平稳运行。 + 第二种是稳中有“进”型:流量平稳运行的同时,偶尔会有一些小脉冲。 + 第三种是周期性“脉冲型”:流量会周期性地变化。 计算、存储与网络:充分利用云的弹性能力 我们前面也有提到,这套架构是完全构建在云原生基础设施之上的,我们在计算、存储、网络上都充分利用了云的弹性能力,具体来看: + 在计算侧,通过 K8s 充分利用 ECS 的弹性能力,通过弹性资源池、HPA 等核心技术支持计算层的快速弹性,并通过跨可用区部署提升可用性。 + 在存储侧,充分利用飞天盘古存储的弹性能力,支持自定义消息的存储时长,冷热数据分离,额外保障冷读的 SLA。 + 在网络侧,公网随开随用,安全和方便兼具,支持多种私网形态接入,并基于 CEN 构建全球互通的消息网络。 在这之上,结合 SRE 平台的智能集群流量调度、集群水位监控、物理预弹性等核心能力,最终交付了一套完整的 ApsaraMQ Serverless 化物理弹性能力。 秒级万 TPS 的极致弹性能力保障 依托于上面的基础物理资源的弹性能力,来看下我们是如何保障秒级万 TPS 的极致弹性能力的? 从两个维度来看用户视角的弹性: + 从限流维度看: 在无损弹性上限以内的 TPS,都不会触发限流;超过无损弹性 TPS 上限后,会有秒级别的限流,通过逻辑弹性调整实例级别的限流阈值后,即可实现最终的 TPS 弹性。 + 从付费角度看: 在预留规格内按规格进行预付费;超过预留规格的上限 TPS,超过部分按量付费,即用多少付多少。 为了满足用户视角的秒级弹性能力,我们通过物理弹性和逻辑弹性相结合的方式来进行保障: + 物理弹性是预弹的机制,弹性时间在分钟级,用户侧无任何感知,由服务侧来 Cover 成本。 + 逻辑弹性是实时生效的,弹性时间在秒级,同时在触发无损弹性 TPS 上限时,用户侧是会有秒级别的限流感知的,另一方面,用户是需要为弹出来的那部分流量进行按量付费的。 两者的关系是:物理弹性是逻辑弹性的支撑,逻辑弹性依赖物理弹性。从弹性流程上看,当用户的流量触发无损弹性上限时,优先判断物理资源是否充足,充足的话就进行秒级逻辑弹性,不充足的话就等待物理资源扩容后再进行逻辑弹性。当然这里会有个预弹的机制,尽量保障逻辑弹性时物理资源都是充足的。 从物理弹性加速来看,通过以下几个方面进行加速: + 计算、存储层按需弹性: 计算层更关注 CPU、客户端连接数等核心指标;存储层更关注内存、磁盘 IO 等性能指标。 + 镜像下载加速: 通过 PlaceHolder + 镜像缓存组件加速新节点的扩容。 + 新增元数据批量创建的机制: 新增存储节点创建 5000 级别的 Topic 下降至 3 秒。 + 新增主动路由更新机制: 降低存储节点物理扩容后承接读写流量的延迟。 我们的系统设计精密,旨在确保用户体验到极致的弹性能力,特别是实现每秒万次事务处理(TPS)的秒级弹性扩展。这一能力的保障策略围绕两个核心维度展开,并深度融合物理与逻辑弹性机制,确保在高吞吐需求下的无缝响应与成本效率。 ApsaraMQ on RDMA:降低计算与存储层之间通信开销 RDMA(全称远程内存直接访问)是一种高性能的网络通信技术,相比 TCP/IP 的网络模式,具有零拷贝、内核旁路、CPU 卸载等核心优势。ApsaraMQ Serverless 化架构具备存算分离的特点,非常适合在计算层和存储层之间引入 RDMA 技术,进一步降低内部组件之间的数据通信开销。 具体来看,计算层 Proxy 在接收到客户端各种协议的消息收发请求以后,通过内置的 Remoting Client 和存储层 Broker 进行数据交换。在原先的 TCP/IP 网络模式中,这些数据是需要从操作系统的用户态拷贝到内核态后,再经由网卡和对端进行交互。依托 RDMA 内核旁路特性,通过实现 RdmaEventLoop 的适配器,消息直接由用户态到 RDMA 网卡和对端进行交互。 从最终 BenchMark 的测试效果来看,引入 RDMA 技术后,存储层 Broker 的 CPU 资源消耗下降了 26.7%,计算层 Proxy 的 CPU 资源消耗下降了 10.1%,计算到存储的发送 RT 下降了 23.8%。 优雅上下线:ApsaraMQ Serverless 弹性如丝般顺滑 在 Serverless 场景下,物理资源的水平弹性扩缩是一个常态化的过程,同时结合 MQ 客户端和计算层 Proxy TCP 长连接的特点,在 Proxy 上下线过程中是比较容易出现连接断开的感知,比如客户端刚发出一个接收消息的请求,连接就被服务侧强行关闭了,是非常容易造成单次请求超时的异常的。 因此,我们通过 gRPC 协议 Http2 的 Go Away 机制以及 Remoting 协议层的扩展,结合 SLB 连接优雅终端的能力来实现 ApsaraMQ 在扩容态、缩容态、以及发布态的无感。 右图展示了缩容态下单台 Proxy 优雅下线的过程: 1. 当收到 Proxy0 需要下线的指令后,SLB 会将 Proxy0 摘除,保障新的连接不再建立到 Proxy0 上,同时 Proxy0 进入 Draining 状态,旧连接进行会话保持。 2. Proxy0 上收到新的请求后,返回的 Response Code 都更新为 Go Away;同时客户单收到 Go Away 的 Response 后,断开原先的连接,向 SLB 发起新建连接的请求。 3. SLB 收到新建连接的请求,会导流至 Proxy1。 4. 等待一段时间后 Proxy0 上的连接会自然消亡,最终达到无感下线的目的。 事件驱动架构赋能 AI 应用 AI 无疑是当今互联网行业的热门话题,同时也是本届云栖大会的核心议题之一。接下来,将阐述面向 AI 原生应用的事件驱动架构如何拥抱 AI,赋能 AI 应用蓬勃发展。 + 面向 AI 应用的实时处理,具备实时 Embedding 至向量数据库、更新私有数据存储的能力,全面提升 AI 应用实时性、个性化和准确度。 + 面向 AI 应用的异步解耦,解耦 AI 推理链路的快慢服务,加快应用响应速度。 + 面向 AI 应用的消息负载,ApsaraMQ 支持主动 Pop 消费模式,允许动态设置每一条消息的个性化消费时长. 同时也支持优先级队列,满足下游多个大模型服务的优先级调度。 + 面向 AI 应用的消息弹性,ApsaraMQ 提供全模式的 Serverless 弹性能力,支持纯按量和预留+弹性、定时弹性等多种流量配置模型; 最后,让我们来看下阿里云事件总线 EventBridge 是如何实现数据实时向量化,提升 AI 应用的实时性和准确度的? 阿里云事件总线的 Event Streaming 事件流处理框架,具备监听多样化数据源,然后经过多次 Transform 之后再投递给下游数据目标的原子能力;依托这个框架,我们是很容易去实现数据的实时向量化的,从 RocketMQ、Kafka、OSS 等多个源监听到实时数据流入之后,经过文本化、切片、向量化等操作,最终存入向量数据库,作为 AI 应用实时问答的多维度数据检索的依据,最终提升了 AI 应用的实时性和准确度。
作者:金吉祥
#行业实践 #云原生

2024年8月13日

谈谈 RocketMQ 5.0 分级存储背后一些有挑战的技术优化
RocketMQ 5.0 提出了分级存储的新方案,经过数个版本的深度打磨,RocketMQ 的分级存储日渐成熟,并成为降低存储成本的重要特性之一。事实上,几乎所有涉及到存储的产品都会尝试转冷降本,如何针对消息队列的业务场景去做一些有挑战的技术优化, 是非常有意思的事。 这篇文章就跟大家探讨下,在消息系统这样一个数据密集型应用的模型下,技术架构选型的分析与权衡,以及分级存储实现与未来演进,让云计算的资源红利真正传达给用户。 1. 背景与需求 RocketMQ 诞生于 2012 年,存储节点采用 sharednothing 的架构读写自己的本地磁盘,单节点上不同 topic 的消息数据会顺序追加写 CommitLog 再异步构建多种索引,这种架构的高水平扩展能力和易维护性带来了非常强的竞争力。 随着存储技术的发展和各种百G网络的普及,RocketMQ 存储层的瓶颈逐渐显现,一方面是数据量的膨胀远快于单体硬件,另一方面存储介质速度和单位容量价格始终存在矛盾。在云原生和 Serverless 的技术趋势下,只有通过技术架构的演进才能彻底解决单机磁盘存储空间上限的问题,同时带来更灵活的弹性与成本的下降,做到 “鱼与熊掌兼得”。 在设计分级存储时,希望能在以下方面做出一些技术优势: 实时: RocketMQ 在消息场景下往往是一写多读的,热数据会被缓存在内存中,如果能做到 “准实时” 而非选用基于时间或容量的淘汰算法将数据转储,可以减小数据复制的开销,利于缩短故障恢复的 RTO。读取时产生冷读请求被重定向,数据取回不需要“解冻时间”,且流量会被严格限制以防止对热数据写入的影响。 弹性: sharednothing 架构虽然简单,缩容或替换节点的场景下待下线节点的数据无法被其他节点读取,节点需要保持相当长时间只读时间,待消费者消费完全部数据,或者执行复杂的迁移流程才能缩容,这种 “扩容很快,缩容很慢” 的形态一点都不云原生,更长久的消息保存能力也会放大这个问题。分级存储设计如果能通过 shareddisk (共享存储) 的方式让在线节点实现代理读取下线节点的数据,既能节约成本也能简化运维。 差异化: 廉价介质随机读写能力较差,类 LSM 的结构都需要大量的 compation 来压缩回收空间。在满足针对不同 topic 设置不同的生命周期(消息保留时间,TTL)等业务需求的前提下,结合消息系统数据不可变和有序的特点,RocketMQ 自身需要尽量少的做格式 “规整” 来避免反复合并的写放大,节约计算资源。 竞争力: 分级存储还应考虑归档压缩,数据导出,列式存储和交互式查询分析能力等高阶技术演进。 2. 技术架构选型 2.1. 不同视角 不妨让我们站在一个新的视角看问题,消息系统对用户暴露的是收发消息,位点管理等一系列的 API,为用户提供了一种能够优雅处理动态数据流的方式,从这个角度说:消息系统拓宽了存储系统的边界。 其实服务端应用大多数是更底层 SQL,POSIX API 的封装,而封装的目的在于简化复杂度的同时,又实现了信息隐藏。 消息系统本身关注的是高可用,高吞吐和低成本,想尽量少的关心存储介质的选择和存储自身的系统升级,分片策略,迁移备份,进一步冷热分层压缩等问题,减少存储层的长期维护成本。 一个高效的、实现良好的存储层应该对不同存储后端有广泛的支持能力,消息系统的存储后端可以是本地磁盘,可以是各类数据库,也可以是分布式文件系统,对象存储,他们是可以轻松扩展的。 2.2. 存储后端调研 幸运的是,几乎所有的“分布式文件系统”或者“对象存储”都提供了“对象一旦上传或复制成功,即可立即读取”的强一致语义,就像 CAP 理论中的描述 “Every read receives the most recent write or an error” 保证了“分布式存储系统之内多副本之间的一致性”。对于应用来说,没有 “拜占庭错误” 是非常幸福的(本来有的数据变没了,破坏了存储节点的数据持久性),更容易做到“应用和分布式存储系统之间是一致的”,并显著减少应用的开发和维护成本。 常见的分布式文件系统有 Ali Pangu,HDFS,GlusterFS,Ceph,Lustre 等。对象存储有 Amazon S3,Aliyun OSS,Azure Blob Storage,Google Cloud Storage,OpenStack Swift 等。他们的简单对比如下: API 支持: 选用对象存储作为后端,通常无法像 HDFS 一样提供充分的 POSIX 能力支持,对于非 KV 型的操作往往存在一定性能问题,例如列出大量对象时需要数十秒,而在分布式文件系统中这类操作只需要毫秒甚至微秒。如果选用对象存储作为后端,弱化的 API 语义要求消息系统本身能够有序管理好这些对象的元数据。 容量与水平扩展: 对于云产品或者大规模企业的存储底座来说,以 HDFS 为例,当集群节点超过数百台,文件达到数亿量级以上时,NameNode 会产生性能瓶颈。一旦底层存储由于容量可用区等因素出现多套存储集群,这种 “本质复杂度” 在一定程度上削弱了 shareddisk 的架构简单性,并将这种复杂度向上传递给应用,影响消息产品本身的多租,迁移,容灾设计。典型的情况就是大型企业为了减少爆炸半径,往往会部署多套 K8s 并定制上层的 Cluster Federation(联邦)。 成本: 以国内云厂商官网公开的典型目录价为例: 本地磁盘,无副本 0.060.08 元/GB/月 云盘,SSD 1元/GB/月,高效云盘 0.35 元/GB/月 对象存储单 AZ 版 0.12 元/GB/月,多 AZ 版本 0.15 元/GB/月,低频 0.08 元/GB/月 分布式文件系统,如盘古 HDFS 接口,支持进一步转冷和 EC。 生态链: 对象存储和类 HDFS 都有足够多的经过生产验证的工具,监控报警层面对象存储的支持更产品化。 2.3. 直写还是转写 方案里,备受瞩目的点在于选择直写还是转写,我认为他们不冲突,两个方案 “可以分开有,都可以做强”。 多年来 RocketMQ 运行在基于本地存储的系统中,本地磁盘通常 IOPS 较高,成本较低但可靠性较差,大规模的生产实践中遇到的问题包括但不限于垂直扩容较难,坏盘,宿主机故障等。 直写: 指使用高可用的存储替换本地块存储,例如使用云盘多点挂载(分布式块存储形态,透明 rdma)或者直写分布式文件系统(下文简称 DFS)作为存储后端,此时主备节点可以共享存储,broker 的高可用中的数据流同步简化为只同步位点,在很大程度上减化了 RocketMQ 高可用的实现。 转写: 对于大部分数据密集型应用,出于故障恢复的考虑必须实时写日志,意味着无法对数据很好的进行攒批压缩,如果仅使用廉价介质,会带来更高的延迟以及更多的内存使用,无法满足生产需要。一个典型思路就是热数据使用容量小的高速介质先顺序写,compation 后转储到更廉价的存储系统中。 直写的目的是池化存储,转写的目的是降低数据的长期保存成本, 所以我认为一个理想的终态可以是两者的结合。RocketMQ 自己来做数据转冷,那有同学就会提出反问了,如果让 DFS 自身支持透明转冷,岂不是更好? 我的理解是 RocketMQ 希望在转冷这个动作时,能够做一些消息系统内部的格式变化来加速冷数据的读取,减少 IO 次数,配置不同 TTL 等。 相对于通用算法,消息系统自身对如何更好的压缩数据和加速读取的细节更加了解。 而且主动转冷的方案在审计和入湖的一些场景下,也可以被用于服务端批量转储数据到不同的平台,到 NoSQL 系统,到 ES,Click House,到对象存储,这一切是如此的自然~ 2.4. 技术架构演进 那么分级存储是一个尽善尽美的最终解决方案吗? 理想很美好,让我们来看一组典型生产场景的数据。 RocketMQ 在使用块存储时,存储节点存储成本大约会占到 30%50%。开启分级存储时,由于数据转储会产生一定的计算开销,主要包括数据复制,数据编解码,crc 校验等,不同场景下计算成本会上升 10%40%,通过换算,我们发现存储节点的总体拥有成本节约了 30% 左右。 考虑到商业和开源技术架构的一致性,选择了先实现转写模式,热数据的存储成本中随着存储空间显著减小,这能够更直接的降低存储成本,在我们充分建设好当前的转写逻辑时再将热数据的 WAL 机制和索引构建移植过来,实现基于分布式系统的直写技术,这种分阶段迭代会更加简明高效,这个阶段我们更加关注通用性和可用性。 可移植性: 直写分布式系统通常需要依赖特定 sdk,配合 rdma 等技术来降低延迟,对应用不完全透明,运维,人力,技术复杂度都有一定上升。保留成熟的本地存储,只需要实现存储插件就可以轻松的切换多种存储后端,不针对 IaaS 做深度绑定在可移植性上会有一定优势。 延迟与性能: 直写模式下存储紧密结合,应用层 ha 的简化也能降低延迟(写多数派成功才被消费者可见),但无论写云盘或者本地磁盘(同区域)延迟都会小于跨可用区的延迟,存储延迟在热数据收发链路不是瓶颈。 可用性: 存储后端往往都有复杂的容错和故障转移策略,直写与转写模式在公有云下可用性都满足诉求。考虑到转写模式下系统是弱依赖二级存储的,更适合开源与非公共云场景。 我们为什么不进一步压缩块存储的磁盘容量,做到几乎极致的成本呢? 事实上,在分级存储的场景下,一味的追求过小的本地磁盘容量价值不大。 主要有以下原因: 故障冗余,消息队列作为基础设施中重要的一环,稳定性高于一切。对象存储本身可用性较高,如果遇到网络波动等问题时,使用对象存储作为主存储,非常容易产生反压导致热数据无法写入, 而热数据属于在线生产业务,这对于可用性的影响是致命的。 过小的本地磁盘,在价格上没有明显的优势。 众所周知,云计算是注重普惠和公平的, 如果选用 50G 左右的块存储,又需要等价 150G 的 ESSD 级别的块存储能提供的 IOPS,则其单位成本几乎是普通块存储的数倍。 本地磁盘容量充足的情况下,上传时能够更好的通过 “攒批” 减少对象存储的请求费用。读取时能够对“温热” 数据提供更低的延迟和节约读取成本。 仅使用对象存储,难以对齐 RocketMQ 当前已经存在的丰富特性, 例如用于问题排查的随机消息索引,定时消息特性等,如果为了节约少量成本,极大的削弱基础设施的能力,反向要求业务方自建复杂的中间件体系是得不偿失的。 3. 分级存储的数据模型与实现 3.1. 模型与抽象 RocketMQ 本地存储数据模型如下: MappedFile:单个真实文件的句柄,也可以理解为 handle 或者说 fd,通过 mmap 实现内存映射文件。是一个 AppendOnly 的定长字节流语义的 Stream,支持字节粒度的追加写、随机读。每个 MappedFile 拥有自己的类型,写位点,创建更新时间等元数据。 MappedFileQueue:可以看做是零个或多个定长 MappedFile 组成的链表,提供了流的无边界语义。Queue 中最多只有最后一个文件可以是 Unseal 的状态(可写)。前面的文件都必须都是 Sealed 状态(只读),Seal 操作完成后 MappedFile 是 immutable(不可变)的。 CommitLog:MappedFileQueue 的封装,每个 “格子” 存储一条序列化的消息到无界的流中。 ConsumeQueue:顺序索引,指向 CommitLog 中消息在 FileQueue 中的偏移量(offset)。 RocketMQ 分级存储提供的数据模型和本地模型类似,改变了 CommitLog 和 ConsumeQueue 的概念: TieredFileSegment:和 MappedFile 类似,描述一个分级存储系统中文件的句柄。 TieredFlatFile:和 MappedFileQueue 类似。 TieredCommitLog 和本地 CommitLog 混合写不同,按照单个 Topic 单个队列的粒度拆分多条 CommitLog。 TieredConsumeQueue 指向 TieredCommitLog 偏移量的一个索引,是严格连续递增的。实际索引的位置会从指向的 CommitLog 的位置改为 TieredCommitLog 的偏移量。 CompositeFlatFile:组合 TieredCommitLog 和 TieredConsumeQueue 对象,并提供概念的封装。 3.2. 消息上传流程 RocketMQ 的存储实现了一个 Pipeline,类似于拦截器链,Netty 的 handler 链,读写请求会经过这个 Pipeline 的多个处理器。 Dispatcher 的概念是指为写入的数据构建索引,在分级存储模块初始化时,会创建 TieredDispatcher 注册为 CommitLog 的 dispatcher 链的一个处理器。每当有消息发送到 Broker 会调用 TieredDispatcher 进行消息分发。下面我们来追踪单条消息进入存储层的流程: 1. 消息被顺序追加到本地 commitlog 并更新本地 max offset(图中黄色部分),为了防止宕机时多副本产生“读摆动”,多副本中多数派的最小位点会作为“低水位”被确认,这个位点被称为 commit offset(图中 2500)。换句话说,commit offset 与 max offset 之间的数据是正在等待多副本同步的。 2. 当 commit offset = message offset 之后,消息会被上传到二级存储的 commitlog 的缓存中(绿色部分)并更新这个队列的 max offset。 3. 消息的索引会被追加到这个队列的 consume queue 中并更新 consume queue 的 max offset。 4. 一旦 commitlog 中缓存大小超过阈值或者等待达到一定时间,消息的缓存将被上传至 commitlog,之后才会将索引信息提交,这里有一个隐含的数据依赖,使索引被晚于原始数据更新。这个机制保证了所有 cq 索引中的数据都能在 commitlog 中找到。宕机场景下,分级存储中的 commitlog 可能会重复构建,此时没有 cq 指向这段数据。由于文件本身还是被使用 Queue 的模型管理的,使得整段数据在达到 TTL 时能被回收,此时并不会产生数据流的“泄漏”。 5. 当索引也上传完成的时候,更新分级存储中的 commit offset(绿色部分被提交)。 6. 系统重启或者宕机时,会选择多个 dispatcher 的最小位点向 max offset 重新分发,确保数据不丢失。 在实际执行中,上传部分由三组线程协同工作。 1. store dispatch 线程,由于该线程负责本地 cq 的分发,我们不能长时间阻塞该线程,否则会影响消息进入本地存储的“可见性延迟”。因此 store dispatch 每次只会尝试对拆分后的文件短暂加锁,如果加锁成功,将消息数据放入拆分后的 commitlog 文件的缓冲区则立即退出,该操作不会阻塞。若获取锁失败则立即返回。 2. store compensate 线程组,负责对本地 cq 进行定时扫描,当写入压力较高时,步骤 1 可能获取锁失败,这个环节会批量的将落后的数据放入 commitlog 中。原始数据被放入后会将 dispatch request 放入 write map。 3. build cq index 线程。write map 和 read map 是一个双缓冲队列的设计,该线程负责将 read map 中的数据构建 cq 并上传。如果 read map 为空,则交换缓冲区,这个双缓冲队列在多个线程共享访问时减少了互斥和竞争操作。 各类存储系统的缓冲攒批策略大同小异,而线上的 topic 写入流量往往是存在热点的,根据经典的二八原则,RocketMQ 分级存储模块目前采用了 “达到一定数据量”,“达到一定时间”两者取其小的合并方式。 这种方式简单可靠,对于大流量的 topic 很容易就可以达到批的最小数据量,对于流量较低的 topic 也不会占用过多的内存。从而减少了对象存储的请求数,其开销主要包括 restful 协议请求头,签名和传输等。诚然,攒批的逻辑仍然存在较大的优化空间,例如 IOT,数据分片同步等各个 topic 流量较为平均的场景使用类似 “滑动窗口” 的加权平均算法,或者基于信任值的流量控制策略可以更好的权衡延迟和吞吐。 3.3. NonStopWrite 特性 NonStopWrite 模型实际上是一致性模型的一部分。实际生产中,后端分布式存储系统的断连和网络问题偶尔会不可避免,而 Append 模型实际上一种强顺序的模型,参考 HDFS 的 23 异步写,我们提出了一种基于 Append 和 Put 的混合模型。 例如:对于如下图片中的 stream,commit / confirm offset = 150,max offset = 200。此时写出缓冲区中的数据包括 150200 的 uncommitted 部分,还有 200 以后源源不断的写入的新数据。 假设后端存储系统支持原子性写入,单个上传请求的数据内容是 150200 这个区间,当单次上传失败时,我们需要向服务端查询上一次写入的位点并进行错误处理。 如果返回的长度是 150,说明上传失败,应用需要重传 buffer。 如果返回的长度是 200,说明前一次上传成功但没有收到成功的 response,提升 commit offset 至 200。 而另一种解决方案是,使用 NonStopWrite 机制立刻新切换一个文件,以 150 作为文件名,立刻重传 150 至 200 的数据,如果有新的数据也可以立刻与这些数据一起上传,我们发现混合模型存在显著优势: 对于绝大部分没有收到成功的响应时,上传是失败的而不是超时,立刻切换文件可以不去 check in 文件长度,减少 rpc 数量。 立刻重传不会阻塞后续新的数据上传,不容易由于后端数据无法写出造成反压,导致前端写失败。 无论 150200 这段数据在第一个文件是到底是写成功还是失败都无关紧要,因为不会去读取这段数据。尤其是对于不支持请求粒度原子写入的模型来说,如果上一次请求的结果是 180,那么错误处理将会非常复杂。 3.4. 随机索引重排 21 年的时候,我第一次听到用“读扩散”或者“写扩散”来描述一个设计方案, 这两个词简洁的概括了应用性能设计的本质。各种业务场景下,我们总是选择通过读写扩散, 选择通过格式的变化,将数据额外转储到一份性能更好或者更廉价的存储, 或者通过读扩散减少数据冗余(减少索引提高了平均查询代价)。 RocketMQ 会在先内存构建基于 hash 的持久化索引文件 IndexFile(非 AppendOnly),再通过 mmap 异步的将数据持久化到磁盘。这个文件是为了支持用户通过 key,消息 ID 等信息来追踪一条消息。 对于单条消息会先计算 hash(topickey) % slot_num 选择 hash slot (黄色部分) 作为随机索引的指针,对象索引本身会附加到 index item 中,hash slot 使用“哈希拉链”的方式解决冲突,这样便形成了一条当前 slot 按照时间存入的倒序的链表。不难发现,查询时需要多次随机读取链表节点。 由于冷存储的 IOPS 代价是非常昂贵的,在设计上我们希望可以面向查询进行优化。新的文件结构类似于维护没有 GC 和只有一次 compation 的 LSM 树,数据结构的调整如下: 1. 等待本地一个 IndexFile 完全写满,规避修改操作,在高 IOPS 的存储介质上异步 compation,完成后删除原来的文件。 2. 从冷存储查询延迟高,而单次返回的数据量大小(不太大的场景)并不会明显改变延迟。compation 时优化数据结构,做到用一次查询连续的一段数据替换多次随机点查。 3. hash slot 的指向的 List 是连续的,查询时可以根据 hash slot 中的 item offset 和 item size 一次取出所有 hashcode 相同的记录并在内存中过滤。 3.5. 消息读取流程 3.5.1 读取策略 读取是写入的逆过程,优先从哪里取回想要的数据必然存在很多的工程考虑与权衡。如图所示,近期的数据被缓存在内存中,稍久远的数据存在与内存和二级存储上,更久远的数据仅存在于二级存储。当被访问的数据存在于内存中,由于内存的速度快速存储介质,直接将这部分数据通过网络写会给客户端即可。如果被访问的数据如图中 request 的指向,存在于本地磁盘又存在于二级存储,此时应该根据一二级存储的特性综合权衡请求落到哪一层。 有两种典型的想法: 1. 数据存储被视为多级缓存,越上层的介质随机读写速度快,请求优先向上层存储进行查询,当内存中不存在了就查询本地磁盘,如果还不存在才向二级存储查询。 2. 由于在转冷时主动对数据做了 compation,从二级存储读取的数据是连续的,此时可以把更宝贵一级存储的 IOPS 留给在线业务。 RocketMQ 的分级存储将这个选择抽象为了读取策略,通过请求中的逻辑位点(queue offset)判断数据处于哪个区间,再根据具体的策略进行选择: DISABLE:禁止从多级存储中读取消息,可能是数据源不支持。 NOT_IN_DISK:不在一级存储的的消息都会从二级存储中读取。 NOT_IN_MEM:不在内存中的消息即冷数据从多级存储读取。 FORCE:强制所有消息从多级存储中读取,目前仅供测试使用。 3.5.2 预读设计 TieredMessageFetcher 是 RocketMQ 分级存储取回数据的具体实现。 为了加速从二级存储读取的速度和减少整体上对二级存储请求数,采用了预读缓存的设计: 即 TieredMessageFetcher 读取消息时会预读更多的消息数据,预读缓存的设计参考了 TCP Tahoe 拥塞控制算法,每次预读的消息量类似拥塞窗口采用加法增、乘法减的流量控制机制。 加法增:从最小窗口开始,每次增加等同于客户端 batchSize 的消息量。 乘法减:当缓存的消息超过了缓存过期时间仍未被全部拉取,此时一般是客户端缓存满,消息数据反压到服务端,在清理缓存的同时会将下次预读消息量减半。 此外,在客户端消费速度较快时,向二级存储读取的消息量较大,此时会使用分段策略并发取回数据。 3.6. 定时消息的分级存储 除了普通消息,RocketMQ 支持设置未来几十天的长定时消息,而这部分数据严重挤占了热数据的存储空间。 RocketMQ 实现了基于本地文件系统的时间轮,整体设计如左侧所示。单节点上所有的定时消息会先写入 rmq_sys_wheel_timer 的系统 topic,进入时间轮,出队后这些消息的 topic 会被还原为真实的业务 topic。 “从磁盘读取数据”和“将消息索引放入时间轮”这两个动作涉及到 IO 与计算,为了减少这两个阶段的锁竞争引入了 Enqueue 作为中转的等待队列,EnqueuGet 和 EnqueuePut 分别负责写入和读取数据,这个设计简单可靠。 不难发现,所有的消息都会进入时间轮,这也是挤占存储空间的根本原因。 写入时,RocketMQ 的分级存储定时消息针对 EnqueuePut 做了一个分流,对于大于当前时间数小时的消息会被写入到基于分级存储的 TimerFlatFile 文件中,我们维护了一个 ConcurrentSkipListMap timerFlatFileTable; 每间隔 1 小时,设置一个 TimerFlatFile,对于 T+n 至 T+n+1 的定时消息,会先被混合追加到 T+n 所对应的文件中。 读取时,当前时间 + 1 小时的消息将被提前出队,这些消息又会重新进入本地 TimerStore 的系统 topic 中/此时,由于定时时间都是将来一小段时间的,他们不再会进入时间轮的结构中。 在这个设计上有一些工程性的考虑: timerFlatFileTable 中的 Key 很多,会不会让分级存储上的数据碎片化?分布式文件系统底层一般使用类 LSM 结构,RocketMQ 只关心 LBA 结构,可以通过优化 Enqueue 的 buffer 让写分级存储时数据达到攒批的效果。 可靠的位点,Enqueue 到“时间轮”和 timerFlatFileTable 可以共用一个 commit offset。对于单条消息来说,只要它进入时间轮或者被上传成功,我们就认为一条消息已经持久化了。由于更新到二级存储本身需要一些攒批缓冲的过程,会延迟 commit offset 的更新,但是这个缓冲时间是可控的。 我们发现偶尔本地存储转储到二级存储会较慢,使用双缓冲队列实现读写分离(如图片中绿色部分)此时消息被放入写缓存,随后转入读缓存队列,最后进入上传流程。 4. 分级存储企业级竞争力 4.1. 冷数据的压缩与归档 压缩是一种经典的时间与空间交换的权衡,其目的在于通过较小的 CPU 开销,实现更少的磁盘占用或网络 I/O 传输。目前,RocketMQ 的热存储在考虑延迟的情况下,仅对单条大于 4K 的消息进行单条压缩存储。对于冷存储服务其实可以做两个层面的压缩与归档处理。 消息队列业务层面,对于大多数业务 Topic,其 Body 通常存在相似性,可将其压缩至原大小的几分之一至几十分之一。 底层存储层面,使用 EC 纠删码,数据被分成若干个数据块,然后再根据一定的算法,生成一些冗余块。当数据丢失时,可以使用其余的数据块和冗余块来恢复丢失的数据块,从而保证数据的完整性和可靠性。典型的 EC 算法后存储空间的使用可以降低到 1.375 副本。 业界也有一些基于 FPGA 实现存储压缩加速的案例,我们将持续探索这方面的尝试。 4.2. 原生的只读挂载能力实现 Serverless 业界对 Serverless 有不同的理解,过去 RocketMQ 多节点之间不共享存储,导致“扩容快,缩容慢”,例如 A 机器需要下线,则必须等普通消息消费完,定时消息全部出队才能进行运维操作。分级存储设计通过 shareddisk的方式实现跨节点代理读取下线节点的数据,如右图所示:A 的数据此时可以被 B 节点读取,彻底释放了 A 的计算资源和一级存储资源。 这种缩容的主要流程如下: 1. RocketMQ 实现了一个简单的选举算法,正常情况下集群内每一个节点都持有对自己数据独占的写锁。 2. 待下线的节点做优雅下线,确保近期定时消息,事务消息,pop retry 消息都已被完整处理。上传自己的元数据信息到共享的二级存储,并释放自己的写锁。 3. 集群使用一定的负载均衡算法,新的节点获取写锁,将该 Broker 的数据以只读的形式挂载。 4. 将原来节点的元数据注册到 NameServer 对客户端暴露。 5. 对于原节点的写请求,例如位点更新,将在内存中处理并周期性快照到共享存储中。 5. 总结 RocketMQ 的存储在云原生时代的演进中遇到了更多有趣的场景和挑战,这是一个需要全链路调优的复杂工程。出于可移植性和通用性的考虑,我们还没有非常有效的使用 DPDK + SPDK + RDMA 这些新颖的技术,但我们解决了许多工程实践中会遇到的问题并构建了整个分级存储的框架。在后续的发展中,我们会推出更多的存储后端实现,针对延迟和吞吐量等细节做深度优化。 参考文档: [1] Chang, F., Dean, J., Ghemawat, S., et al. Bigtable: A distributed storage system for structured data. ACM Transactions on Computer Systems, 2008, 26(2): 4.[2] Liu, Y., Zhang, K., & Spear, M. DynamicSized Nonblocking Hash Tables. In Proceedings of the ACM Symposium on Principles of Distributed Computing, 2014.[3] Ongaro, D., & Ousterhout, J. In Search of an Understandable Consensus Algorithm. Proceedings of the USENIX Conference on Operating Systems Design and Implementation, 2014, 305320.[4] Apache RocketMQ. GitHub, _https://github.com/apache/rocketmq_[5] Verbitski, A., Gupta, A., Saha, D., et al. Amazon aurora: On avoiding distributed consensus for i/os, commits, and membership changes. In Proceedings of the 2018 International Conference on Management of Data, 2018, 789796.[6] Antonopoulos, P., Budovski, A., Diaconu, C., et al. Socrates: The new sql server in the cloud. In Proceedings of the 2019 International Conference on Management of Data, 2019, 17431756.[7] Li, Q. More Than Capacity: Performanceoriented Evolution of Pangu in Alibaba. Fast 2023_https://www.usenix.org/conference/fast23/presentation/liqiangdeployed_[8] Lu, S. Perseus: A FailSlow Detection Framework for Cloud Storage Systems. Fast 2023
作者:斜阳
#技术探索 #云原生

2024年7月24日

Apache RocketMQ,构建云原生统一消息引擎
演讲嘉宾:林清山(花名:隆基),Apache RocketMQ 联合创始人,阿里云资深技术专家,阿里云消息产品线负责人。国际消息领域专家,致力于消息、实时计算、事件驱动等方向的研究与探索,推进 RocketMQ 云原生架构、超融合架构的演进。 本文整理于 2023 年云栖大会林清山带来的主题演讲《Apache RocketMQ 云原生统一消息引擎》 Apache RocketMQ 简介 消息队列演进趋势 操作系统、数据库、中间件是基础软件的三驾马车,而消息队列属于最经典的中间件之一,已经有 30 多年的历史。它的发展主要经历了以下几个阶段: 第一个阶段,2000 年之前。80 年代诞生了第一款消息队列是 The Information Bus,第一次提出发布订阅模式来解决软件之间的通信问题;到了 90 年代,则是国际商业软件巨头的时代,IBM、Oracle、Microsoft 纷纷推出了自己的 MQ,其中最具代表性的是 IBM MQ,价格昂贵,面向高端企业,主要是大型金融、电信等企业;这类商业 MQ 一般采用高端硬件,软硬件一体机交付,MQ 本身的软件架构是单机架构。 第二阶段,20002007 年。进入 00 年代后,初代开源消息队列崛起,诞生了 JMS、AMQP 两大标准,与之对应的两个实现分别为 ActiveMQ、RabbitMQ,他们引领了初期的开源消息队列技术。开源极大的促进了消息队列的流行、降低了使用门槛,技术普惠化,逐渐成为了企业级架构的标配。相比于今天而言,这类 MQ 主要还是面向传统企业级应用,面向小流量场景,横向扩展能力比较弱。 第三阶段,20072017 年。PC 互联网、移动互联网爆发式发展。由于传统的消息队列无法承受亿级用户的访问流量和海量数据传输,诞生了互联网消息中间件,核心能力是全面采用分布式架构、具备很强的横向扩展能力,开源典型代表有 Kafka、RocketMQ,闭源的还有淘宝 Notify。Kafka 的诞生还将消息中间件从消息领域延伸到了流领域,从分布式应用的异步解耦场景延伸到大数据领域的流存储和流计算场景。 第四阶段,2014至今。云计算、IoT、大数据引领了新的浪潮。 Apache RocketMQ 发展历程 伴随着消息队列行业的发展,Apache RocketMQ 自身也发展了十年,可分为“诞生于互联网”与“成长于云计算”两大阶段。 第一个阶段是 RocketMQ 的从 0 到 1,在阿里内部规模化落地。2012 年,为了支撑超大规模电商互联网架构,阿里中间件研发了 RocketMQ,并在产品诞生初期开源,2017 年 RocketMQ 统一了阿里消息技术体系。 第二个阶段是云计算 , 2016 年 RocketMQ 上云,这也是业界首个提供公共云 SaaS 形态的开源消息队列。2016 年,阿里把 RocketMQ 捐赠给 Apache,17 年孵化毕业,成为国内首个 TLP 的互联网中间件。在云计算和开源双轮驱动下,RocketMQ 在阿里外部完成全面规模化,帮助千行百业完成数字化转型,产品能力也得到进一步的飞跃。2022 年 5.0 正式发布,Apache RocketMQ 正式迈进云原生时代。 Apache RocketMQ 5.x 统一消息引擎 Apache RocketMQ 5.X 业务全景 为了满足云时代多样化的用户需求,RocketMQ 5.0 从原来的互联网业务消息中间件,扩展到"消息、事件、流"超融合处理平台,解锁更全面的能力。 在消息领域,全面拥抱云原生技术,更好的弹性架构和高可用能力。 在事件领域,支持 CloudEvent 规范,以事件为中心的产品新界面,助力客户建设跨业务、跨组织的数字化商业生态。 在流领域,流存储增强批量特性,大幅度提高数据吞吐量;新增逻辑队列能力,解耦逻辑资源和物理资源,在流场景也具备无缝伸缩能力;新增流数据库 RSQLDB,提供实时事件流处理、流分析能力。 RocketMQ 基于端云一体化架构实现了完整的物联网消息队列的能力,从原来的连接应用扩展到连接物联网设备。同时 RocketMQ 5.0 也继续保持极简架构的原则,能够以最低的资源消耗、运维成本搭建服务,适合边缘计算。 为什么说 Apache RocketMQ 是统一的消息引擎,主要有以下几方面的统一。 消息和流的统一 第一个统一是 Apache RocketMQ 统一了消息和流的场景。 通过这个对比图来看,消息和流的区别。常说的消息场景、队列场景侧重于业务集成,在这个场景里 RocketMQ 的主要作用是连接业务应用,解耦业务架构的上下游系统,比如交易系统的解耦。这类场景,更多的是在线业务,由用户触发某个业务流程,比如购买。为了保障用户体验,消息系统要优先保障低延迟。这个场景里和同步通信 RPC 对应,消息系统承担都是异步通信职责。在消息消费层面,更多的是基于消息数据执行对应的业务逻辑,触发下一个业务流程。每条消息的处理都是不相关的,无状态的。侧重于业务数字化场景,可类比于数据库的 OLTP,单次操作数据量少,用于在线交易。 再来看流场景的话,它主要是侧重于数据集成,连接各种数据组件,进行数据分发,解耦数据架构的上下游系统。比如日志解决方案,采集日志数据,进行ETL将日志数据分发到搜索引擎、流计算、数据仓库等。除了日志之外,数据库 Binlog 分发、页面点击流也是常见的数据源。在这种场景里里面,由于是离线业务,它对低延迟的需求较弱,更加侧重于大批量吞吐型负载。另外在消息消费阶段,不再是单条消息处理,更多的是批量转储,或者批量进行流计算。侧重于数字业务化场景,可类比于数据库的 OLAP,单次操作数据量大,用于离线分析场景。 具体来说,RocketMQ 如何实现消息和流的统一呢? 主要体现在领域模型的 统一,包含 Producer、Consumer、Topic、Topic 逻辑分区 MessageQueue。在统一的领域模型下采用不同的访问模式来满足消息和流的不同场景。 在消息场景,客户端只感知 Topic,往 Topic 发送消息,基于订阅关系消费Topic的消息,触发对应的业务逻辑,返回消费成功或者失败,消费失败还会有重试。 而在流的场景,对于消息数据的访问模式有所不同。由于是用在数据集成的场景,对于大规模的数据集成,不可避免的要涉及到数据的分片,基于数据分片来连接上下游数据系统。在消息的读写方式上,不再是指定 Topic 读写,而是指定 Topic 分片,也就是队列进行读写操作。作为流存储系统,下游的消费通常会是一些流计算引擎,用于有状态计算。为了支撑流计算引擎的容错处理,它需要支持 checkpoint 机制,类似于为流计算引擎提供 redolog,能够按照队列位点重放消息,重新恢复流计算的状态。他也会要求分片内有序,同一个 key 的数据会 hash 到同一个分片,用于实现 keyby 的操作。 这个就是流存储访问模式跟消息访问模式的区别。在消息场景里,用户只需要关注到 topic 资源,无需了解队列、位点等概念。 在流场景里面,还有一个很重要的变化,就是数据类型的变化。 做个简单对比,业务集成场景,消息的数据承载的是业务事件,比如说订单操作、物流操作,它特点就是数据规模较小,但是它每一条数据的价值都特别高,它的访问模式是偏向于在线的,单条事务的短平快访问模式。 而在流的场景里面呢,它更多的是一些非交易型的数据。比如说用户日志,系统的监控、IoT 的一些传感器数据、网站的点击流等等。他的特点是数据规模有数量级的提升,但单条数据的价值比较低的,然后它的访问模式偏向于离线批量传输。所以在流的场景里面,RocketMQ 存储要面向高吞吐做更多的优化。 在 RocketMQ 5.0 里面, 引入了端到端的批量消息。从客户端开始,在发送阶段,消息在客户端攒批到一定数量,直接 1 个 RPC 请求里面直接发到 broker 端。broker 存储阶段,直接把整批消息存储,用批量索引的技术,一批消息只会构建一个索引,大幅度提升索引构建速度。在消费阶段,也是按照整批数据读取到消费端,先进行解包操作,最后执行消费逻辑。这样整个 Broker 的消息 TPS 可以从原来的 10 万级提升至百万级。 端和云的统一 第二个统一是端和云的统一,端指物联网设备端、移动端,云指云端服务和应用。 我们先来了解一下物联网的场景是什么,以及消息在物联网里面有什么作用。物联网肯定是最近几年最火的技术趋势之一,有大量的研究机构、行业报告都提出了物联网快速发展的态势。 物联网设备规模爆发式增长,会在 2025 年达到 200 多亿台。 物联网的数据规模,来自物联网的数据增速接近 28%,并且未来有 90% 以上的实时数据来自物联网场景。这也就意味着未来的实时流数据处理数据类型会有大量物联网数据。 重要的趋势是边缘计算,未来会有 75% 的数据在传统数据中心或者云环境之外来处理,这里的边缘指的是商店、工厂、火车等等这些离数据源更近的地方。 通过这个图能看出消息在物联网场景发挥的作用: 第一个作用是连接,承担通信的职责,支持设备和设备的通信,设备和云端应用的通信,比如传感器数据上报、云端指令下发等等这些功能,支撑 IoT 的应用架构,连接云边端。 第二个作用是数据处理,物联网设备源源不断的产生数据流,有大量需要实时流处理的场景,比如设备维护,高温预警等等。基于 MQ 的事件流存储和流计算能力,可以构建物联网场景的数据架构。 在一个完整的物联网解决方案中,会同时涉及到端和云的协同,端用于采集数据、执行设备指令,云用于存储数据、分析数据,执行复杂业务逻辑。所以在 RocketMQ 5.0 里发布了 MQTT 子产品,实现端云一体化。它有三个核心特点: 1. 采用标准的物联网协议 MQTT,该协议面向物联网弱网环境、低算力的特点设计,协议十分精简。同时有很丰富的特性,支持多种订阅模式,多种消息的 QoS,比如有最多一次,最少一次,当且仅当一次。它的领域模型设计也是 消息、 主题、发布订阅等等这些概念,和 RocketMQ 特别匹配,这为打造一个云端一体的 RocketMQ 产品形态奠定了基础。 2. 采用端云一体化的架构,因为领域模型接近、并且以 RocketMQ 作为存储层,每条消息只存一份,这份消息既能被物联网设备消费,也能被云端应用消费。另外 RocketMQ 本身是天然的流存储,流计算引擎可以无缝对 IoT 数据进行实时分析。消息可以来自各个接入场景(如服务端的 RocketMQ,设备端的 MQTT),但只会写一份存到 commitlog 里面,然后分发出多个需求场景的队列索引,比如服务端场景(RocketMQ)可以按照一级 Topic 队列进行传统的服务端消费,设备端场景可以按照 MQTT 多级 Topic 以及通配符订阅进行消费消息。这样就可以基于同一套存储引擎,同时支持服务端应用集成和 IoT 场景的消息收发,达到端云一体化。 3. 将原来 RocketMQ 的万级队列能力提升到百万级队列能力。例如 Kafka 这样的消息队列每个Topic 是独立文件,但是随着 Topic 增多消息文件数量也增多,顺序写就退化成了随机写,性能明显下降。RocketMQ 在 Kafka 的基础上进行了改进,使用了一个 Commitlog 文件来保存所有的消息内容,再使用 CQ 索引文件来表示每个 Topic 里面的消息队列,因为 CQ 索引数据比较小,文件增多对 IO 影响要小很多,所以在队列数量上可以达到十万级。但是这个终端设备队列的场景下,十万级的队列数量还是太小了, 希望进一步提升一个数量级,达到百万级队列数量,所以, 引入了 Rocksdb 引擎来进行 CQ 索引分发,实现了百万级队列。 消息和事件的统一 第三个统一是消息和事件的统一。 在这之前, 我们先了解一下什么是事件驱动。事件驱动本质上是一种软件设计模式,它能够最大化降低不同模块以及不同系统之间的耦合度。 下面是一个典型的事件驱动架构图,首先是事件生产者发送事件到 EventBroker,然后 EventBroker 会把事件路由到对应的消费者进行事件处理。事件处理能够灵活扩展,随时增减事件消费者,事件生产者对此透明。 事件驱动架构其实是个很经典的设计模式,因为早在几十年前,就出现过多种事件驱动的技术。比如桌面客户端编程框架,点击按钮就可以触发 onclick 事件,开发者编写业务逻辑响应事件;在编程语言上,也经常会采用事件驱动的代码模式,比如 callback、handler 这类的函数;进入分布式系统的时代,系统之间的通信协同也会采用事件驱动的方式。 从这个图我们可以发现事件驱动架构其实和基于消息的应用解耦差别不大,他们本质上要解决的都是解耦的问题。无论是消息的发布订阅,还是事件的生产消费都是为了进行代码解耦、系统解耦。消息队列更偏技术实现,大部分的 EventBroker 都是基于消息队列实现的,而事件驱动更偏向于架构理念。 事件驱动跟消息驱动最大的区别就是,事件是一种特殊的消息,只有消息满足了某些特征,才能把它叫做事件。 打个比方,来看上面这个图。消息就像是一个抽象类,有多种子类,最主要的就是 Command 和 Event 两种。以信号灯为例,向信号灯发送打开的消息,这就是一种 Command,信号灯接受这个 Command 并开灯。开灯后,信号灯对外发出信号灯变成绿色的消息,这个就是一种 Event。 对于 Event 来说,有四个主要的特征: 1. 它是一个不可变的,事件就是表示已经发生了的事情,已经成为事实。 2. 事件有时间概念,并且对同一个实体来说事件的发送是有序的。如信号灯按顺序发送了绿、黄、红等事件 3. 事件是无预期的,这个就是 EDA 架构之所以能够实现最大化解耦的特点,事件的产生者对于谁是事件消费者,怎么消费这个事件是不关心的 4. 由于事件驱动是彻底解耦的,并且对于下游怎么去消费事件没有预期,所以事件是具象化的,应该包括尽可能详尽的信息,让下游消费者各取所需。这就是消息和事件的区别。 走向 Serverless Serverless 大势 Serverless 被认为是下一代的云原生代表技术;云原生的本质则是通过一套技术体系和方法论,帮助客户更好的用云,充分释放云计算红利,让使用云计算的客户能够实现更高效、更低成本的数字化转型。关于云原生的技术, 听的比较多有微服务、容器化等。微服务侧重于应用架构理念的革新,用微服务来实现业务高内聚、低耦合,从而提高研发效率、协作效率、业务敏捷度;而容器化则涉及应用运维层面,用容器化来屏蔽基础设施的差异,提高可移植性,借助 K8S 平台,还能提高应用的运维效率、资源利用率。 而 Serverless 在云原生所代表的含义则是,基础技术下沉,云服务界面上移的趋势,本质上还是让客户把更多的精力聚焦在业务研发上,无需关心底层技术和物理资源。 如下面这个图,在云计算之前,用户需要自建 IDC、购买物理机、自行虚拟化、搭建中间件,然后才能进行业务研发,有大量的时间、精力、资源都花在和业务无关的项目上。进入云计算之后,越来越多的基础设施由云厂商来提供,从最早的 IaaS,直接使用云厂商的计算、存储、网络资源;再到 PaaS,无需自建数据库和中间件,直接使用托管基础软件服务;再到现在云计算演进到 Serverless 的阶段,客户完全把大部分精力聚焦在业务代码的开发上。 对云服务厂商来说,Serverless 的云产品也从最早的少数产品如对象存储、函数计算等,发展到现在的 all on Serverless,具备了完备的 Serverless 产品体系,如 Serverless 消息队列、微服务、数据库、搜索、大数据产品等。 全面 Serverless 的应用场景 进入 Serverless 时代,全面使用 Serverless 的客户会为消息队列带来哪些场景变化呢? 如在应用侧,越来越多的应用不在部署在自行购买的 ECS 上面,直接托管在 Serverless 容器、应用引擎、函数计算上,云服务会根据其业务流量或者系统负载自动进行弹性伸缩,对应的消息服务也要能根据消息流量自行弹性伸缩。 在车联网消息解决方案场景里,汽车每天都有早晚高峰,上下行的消息流量也出现明显的波峰波谷,车联网客户无需为波峰流量预先购买消息资源,而是根据实际消息量,用多少付多少钱。 在移动 App 推送场景,也会面临更多维度的资源指标,比如需要维持大量的连接数、偶尔的峰值消息推送、极小的消息存储空间,客户无需预先购买计算、存储、网络绑定的消息实例,而是分别面向连接数、消息流量、存储空间分别付费。 除了核心的弹性能力之外,消息队列的核心架构场景“事件驱动”在 Serverless 时代成为最重要的架构模式,事件驱动架构有助于开发更加敏捷、可扩展、韧性的 Serverless 应用,事件驱动天然匹配 Serverless 研发范式。因此 Serverless 全云开发模式中,客户希望消息队列的服务界面也需要上移,具备“事件总线”的能力。客户不仅需要开箱即用的 Serverless 云服务,也需要开箱即用的事件驱动集成服务,无需像以前一样编写集成的胶水代码,研发效率进一步提升,走向 lowcode、nocode。比如云产品事件集成,OSS 文件上传事件发送到事件总线,用户订阅这个事件,并基于函数计算进行文件加工处理响应事件,驱动 Serverless 算力。 面向 Serverless 的趋势,RocketMQ 5.0 从产品形态到技术架构都做了巨大的演进。 面向 Serverless 应用的新 SDK 当应用大量使用 Serverless 技术之后,应用的实例数将会随着流量的变化动态弹性伸缩,相比于过去的场景,实例数变化将十分频繁,这就对消息收发的负载均衡提出比较大的挑战。 先来看生产链路的负载均衡,生产者通过服务发现机制,知道了 Topic 的数据分片以及对应的 Broker 地址。他的服务发现机制是比较简单的,在默认情况下采用 RoundRobin 的方式轮询发送到各个 Topic 队列,保证了 Broker 集群的流量均衡。生产者是彻底无状态的,所以无论如何弹性伸缩,都没有太多影响。 再来看下消费者的负载均衡,相对来说它会比生产者更复杂,旧模式是队列级负载均衡,消费者知道Topic的队列总数,也知道同一个 ConsumerGroup 下的实例数,就可以按照统一的分配算法,类似一致性 hash 的方式,让每个消费者实例绑定对应的队列,只消费绑定队列的消息,每个队列的消息也只会被一个消费者实例消费。这种模式最大的缺点就是负载不均衡,消费者实例要绑定队列、有临时状态。当应用实例数变化频繁的时候,这种负载不均衡会导致应用的 Serverless 扩容无效,扩容的新阶段无法分担消息的流量。如图 Topic 有 2 个分区,扩容第三个节点会出现空跑;如果 把 Topic 扩容成 3 个分区,随后消费者实例又缩容回 2 个,那么就会出现其中一个消费者实例承担三分之二的流量,出现过载。 所以在 RocketMQ 5.0 里面, 引入了消息粒度的负载均衡机制,无需绑定队列,消息在消费者集群随机分发,这样就可以保障消费者集群的负载均衡。更重要的是这种模式更加符合全链路 Serverless 化的场景,Broker 的机器数、Topic 的队列数和消费者实例数完全解耦,可以独立扩缩容。 Serverless事件驱动的挑战 在上一个章节, 提到消息和事件的统一,事件是一种包含业务语义的消息。下面结合一个典型事件驱动的案例来看看。如下图是一个基于消息队列 RocketMQ 实现的一个交易系统,采用事件驱动的架构,围绕“订单”事件完成交易业务。事件生产者是交易中心,消费者是交易的下游系统。比如发送订单创建事件,购物车响应事件,删除之前的加购商品;发生订单付款事件,会员系统响应事件,给客户增加积分,物流系统响应事件,执行后续的发货履约环节。整个交易系统是由“事件驱动”的微服务构建而成。 基于经典消息队列的事件驱动方案在一个组织内部、部门内部是一个不错的选择。但是在 Serverless 时代面临很多全新的挑战。 越来越多的商业数字化解决方案是由多个不同组织协作完成的,比如 SaaS 平台(钉钉)和它的合作伙伴,钉钉平台发布各种事件,包括视频会议、日程、通讯录、审批流、钉盘等事件,下游合作伙伴消费这些事件,研发行业应用。在这类新型数字化解决方案中,往往事件的生产者和消费者属于不同的公司,开发者无法进行密集的交流,低成本的了解“事件”定义、格式、使用方法。目前的模式过于依赖开发者之间的交流,以及公司的内部文档沉淀。 不同的公司往往会使用不同的技术体系,比如使用不同的消息队列,事件生产者使用 RocketMQ,事件消费者使用 RabbitMQ;比如使用不同的消息传输协议,HTTP 或 AMQP。 事件的消费者多样化,哪怕是同一个业务的事件,事件消费者可能只需要其中的某种子类型;哪怕是同一个事件,事件消费者也可能只能访问其中的部分字段。 缺少开箱即用的事件集成能力,客户全面用云后,需要响应各种云产品事件,比如响应 OSS 上传事件,使用函数计算对文件进行处理,这种预先集成的特性,经典的消息队列不具备。 Serverless 的事件驱动技术需要更加彻底的解耦,只关注“事件”本身,解耦技术实现细节,如传输协议、SDK、生产消费模式。 Serverless 事件驱动的设计 为了实现 Serverless 的事件驱动, 在消息队列的基础上面,将“事件驱动”场景的服务界面上移,围绕“事件”的领域模型进行重新设计。 最左边是事件源,由于事件需要具备跨平台生产消费的能力,所以采用 CNCF 的 CloudEvents 来作为事件的格式。这个是业界事件的事实标准,它标准简化了事件声明,提升事件在跨服务、跨平台的互操作性。 由于事件是有可能被跨组织消费的,所以需要一个统一的事件中心,让这些不同的事件源都注册到这个事件中心。对消费者来说就好比是一个事件商店,能够选择自己感兴趣的事件订阅。 在事件消费者开始编写消费逻辑的时候,开发者还需要对这个事件的格式有更清楚的了解,需要知道这个事件有哪些内容,有哪些字段,分别是什么含义,才能编写正确的消费业务逻辑。所以,事件总线还提供了 schema 中心,有这个 schema 中心后,消费者对于事件的格式也就一目了然,不用跟事件源的发起者进行沟通了,整个效率也得到了大幅度的提升。 再往后面看,就到了事件消费的环节,因为事件的消费者种类很多,不同消费者关注不同的事件类型,事件总线需要提供丰富的过滤规则。即便多个消费者对同一个事件感兴趣,但是可能只需要事件的部分内容,事件总线还提供了事件转换的能力。这就是 RocketMQ 5.0 对事件驱动的能力抽象。 Serverless 事件驱动的新形态 基于上面的全新设计, 以 RocketMQ 作为事件存储的内核,实现了全新的事件总线 EventBridge。在产品界面上,面向事件驱动的业务进行一层抽象,核心领域对象从消息变成 CloudEvents。基于统一事件标准来构建事件驱动的数字生态。 事件源是多样化的,可以是云产品事件、数据流事件、也可以是 SaaS 平台事件,应用自定义事件、通用的 WebHook。当然,它的事件目标也是多样化的,通过事件规则引擎把事件路由到不同的消费者,典型的消费者包括函数计算、消息系统(用于解耦生产者、消费者使用不同的消息队列技术)、存储系统、流计算引擎、通用的 webhook,甚至可以是消息通知如语音\短信\邮件。事件驱动架构更适合建设混合云、多云的数字化系统。 通过 EventBridge 实现彻底的事件驱动架构,真正做到只关心“事件”本身,生产者和消费者实现更加彻底的解耦,包括组织解耦、技术体系解耦。 面向 Serverless 消息内核的重构 前面提到的主要是面向 Serverless 应用场景,如一些 Serverless 化的应用,Serverless 化的事件驱动架构,RocketMQ 在产品形态、使用界面上做出的改变。现在我们从技术架构演进的角度来看 RocketMQ 如何实现一个 Serverless 化的消息云服务。在 Serverless 场景下,客户需要的是声明式的逻辑资源,不同逻辑资源可以解绑,分别弹性、按量服务。 面向 Serverless 的场景,RocketMQ 演进到三层存算分离的架构。 第一层是 RocketMQ proxy,它主要承载的是多协议,多领域场景的覆盖。这里面的领域场景有 RocketMQ 场景,经典的服务端应用集成;还有 MQTT,面向物联网的应用;还有 EventBridge 面向事件驱动型的应用。Proxy 可以认为是计算资源的主要载体,它是一个彻底的无状态的网关。它可以面向客户不同的连接数,不同的消息 TPS 以及不同的消息的读写的比例的变化,进行一个计算、网络资源的独立弹性。这样才可以匹配到客户在 Serverless 场景下,对多种资源解耦弹性的需求。 第二层是 RocketMQ 的存储引擎,它主要专注于消息多副本实现、多副本如何进行高可用切换。同时它也要负责本地存储跟云存储的统一抽象。由于消息的存储主要在云盘和对象存储上面,大部分的消息数据存储在对象存储,store 自身的状态被弱化了,弹性效率也得以提升。RocketMQ store 可以根据客户的消息流量特点,如消息吞吐量、TPS、消息大小、批量因素等和存储资源 IOPS、带宽、存储空间进行弹性匹配,实现消息存储和计算的解耦。 第三层是云存储层这一块,大部分的消息存储在对象存储上,这是公共云基础设施级的存储池化。通过将冷数据卸载到了对象存储,然后缩短了 RocketMQ Store 的生命周期,同时也具备一个低成本的无限消息存储空间。 现在 RocketMQ 5.0 已经具备弹性架构,采用云服务形态的 RocketMQ 能够进一步和云的基础设施深度结合,充分释放云计算红利。 在计算层面,RocketMQ 5.0 通过容器服务充分利用 ECS 弹性能力,采取弹性资源池 + HPA 相关技术支持计算能力快速弹性,同时 ACK 自带的跨可用区部署能力为云产品提供了充足的高可用保障。 在网络层面,RocketMQ 5.0 可接入了多种云原生网络能力,满足用户对多样性网络的需求,公网随开随用,支持多种私网网络形态,基于 CEN 构建了全球互通的消息网络,实现异地多活。 在存储方面,基于盘古 DFS、对象存储的多级存储架构,提供低成本的无限存储能力,冷热数据链路分离,提供更高的 SLA。 事件驱动赋能 Serverless 技术栈 最后,基于 Apache RocketMQ 打造的消息产品体系,以事件驱动 + 集成两大场景,赋能全面 Serverless 技术栈。 以上是一个典型的 Serverless 产品体系,一些头部云厂商已经实现了核心产品的全面 Serverless 化,无论是计算、存储,还是大数据分析都具备了 Serverless 服务能力,基于这些能力客户能够打造端到端的 Serverless 应用,聚焦核心业务,把降本增效做到极致。
作者:隆基
#强力推荐 #云原生

2023年7月20日

从互联网到云时代,Apache RocketMQ 是如何演进的?
2022年,RocketMQ5.0的正式版发布。相对于4.0版本而言,架构走向云原生化,并且覆盖了更多业务场景。 一、消息队列演进史 操作系统、数据库、中间件是基础软件的三驾马车,而消息队列属于最经典的中间件之一,已经有30多年的历史。消息队列的发展主要经历了以下几个阶段: 第一阶段(19802000年):80年代诞生了第一款消息队列The Information Bus,第一次提出发布订阅模式来解决软件之间的通信问题;90年代是国际商业软件巨头的时代,IBM、Oracle、Microsoft纷纷推出自己的MQ,其中最具代表性的为IBM MQ,价格昂贵,面向高端企业,主要是大型金融、电信等企业。该类商业MQ一般采用高端硬件,软硬件一体机交付,MQ本身的软件架构为单机架构。 第二阶段(2000~2007年):进入00年代后,初代开源消息队列崛起,诞生了JMS、AMQP两大标准,与之对应的两个实现分别为ActiveMQ、RabbitMQ,他们引领了初期的开源消息队列技术。开源极大促进了消息队列的流行,降低了使用门槛,技术普惠化,逐渐成为企业级架构的标配。相比于今天而言,这类MQ主要面向传统企业级应用和小流量场景,横向扩展能力较弱。 第三阶段(2007~2017年):PC互联网、移动互联网爆发式发展。由于传统的消息队列无法承受亿级用户的访问流量与海量数据传输,诞生了互联网消息中间件,核心能力是全面采用分布式架构,具备很强的横向扩展能力,开源典型代表有Kafka、RocketMQ,闭源的有淘宝Notify。Kafka的诞生还将消息中间件从消息领域延伸到了流领域,从分布式应用的异步解耦场景延伸到大数据领域的流存储与流计算场景。 第四阶段(2014~至今):云计算、IoT、大数据引领了新的浪潮。 二、互联网时代的RocketMQ 阿里的电商系统最初是个庞大的单体巨石应用,在研发效率、稳定性方面都无法满足淘宝和天猫飞速的发展。为了解决问题,2008年,淘宝与天猫发起了一次最大规模的架构升级,启动了“五彩石”项目,将单体应用拆分为分布式应用,同时抽象淘宝、天猫的共同底座——业务中台,包括交易中心、商品中心、买家中心等。在业务中台之下,同时诞生了阿里中间件(初期三大件包括消息、RPC、分布式数据层),RocketMQ是其中之一。 虽然在当时业界已经存在不少商业或开源的消息队列,比如IBMMQ、ActiveMQ、RabbitMQ,但无一例外,它们都诞生于传统企业级应用的场景,无法承受互联网对于高并发、无限扩展的苛刻要求。以RabbitMQ为例,RabbitMQ的队列流量与存储负载都为单机,无法满足业务横向扩展的需求。当时另一款具备无限横向扩展能力的消息队列是Kafka,但其主要用于日志类场景,未经过大规模核心业务稳定性验证,而且偏向于简单的log型消息队列,无法满足电商对于复杂消息功能特性的诉求,比如消息过滤、延迟消息等。 另一方面,传统的消息队列无法解决电商业务对于分布式一致性的要求。通过消息队列实现应用异步解耦后,电商业务还需要保障不同上下游应用对于订单状态要达成最终一致,否则会产生大量脏数据,造成业务错误。 大规模的电商系统,既要高性能又要一致性,传统的分布式事务技术束手无策。比如IBM MQ虽然可以使用XA事务来满足分布式一致性的功能诉求,但是XA带来的延迟与成本,对于海量的互联网流量难以承受。 为了解决电商业务对于消息队列的高性能、一致性、无限扩展等需求,自研消息队列成为了当时阿里唯一的出路,最终互联网消息队列RocketMQ应运而生。 为了支持超大规模的复杂电商业务,RocketMQ面向四个方面进行了重点建设,形成了四大优势能力。 ① 支撑超大规模复杂业务的能力,具备丰富的消息特性。 每一个大型互联网公司都会有主营业务(比如阿里是交易、蚂蚁是支付、饿了么是外卖),以主营业务为中心扩展业务能力,阿里电商是围绕交易事件建设的电商操作系统,每笔交易事件都会触发不同的业务,不同细分业务会关注不同类型的交易事件,比如垂直市场只关注某个类目的交易事件、天猫超市只关注某个卖家的交易事件、购物车只关注下单成功的交易事件等。 RocketMQ的SQL订阅提供灵活的消息过滤能力,能够满足下游消费者按照不同的业务维度进行消息过滤的诉求。 在大型互联网业务中,还会有各种定时事件触发场景,最典型的是交易超时关闭机制,阿里交易或者12306订票都有类似的机制。RocketMQ的定时消息能够很方便的满足这类诉求。 ② 一致性。 无论是阿里交易还是蚂蚁支付,都天然对数据一致性有着极高要求,RocketMQ在一致性方面也打造了多个关键特性。最具代表性的是分布式事务消息,RocketMQ是第一个实现该种特性的消息队列,能够保障交易的上下游对于订单状态达到最终一致。该方案也成为异步消息一致性方案的事实标准,被多个互联网公司所采纳,甚至也有公司将移植到定制版的Kafka种。除了分布式一致性之外,RocketMQ还提供了顺序消息的特性,满足顺序一致性的需求。 ③ 稳定性。 稳定性是交易与金融场景的基石特性,也是RocketMQ的根本。RocketMQ除了具备核心服务的HA之外,还具备了全局高可用能力,在阿里内部支持同城多活、异地多活、中心容灾等高阶HA能力。同时,稳定性也不局限于数据与服务的高可用,RocketMQ从产品层面对稳定性进行了全方位的建设,如消息轨迹、消息回溯、消息死信机制。 ④ 高性能。 在双十一的极限流量下,RocketMQ写消息延迟4个9在1ms内,100%在100ms内。RocketMQ采用sharednothing分布式架构,在吞吐量方面也具备无限扩展的能力,已经连续10年支持了双十一万亿级消息洪峰,为百万级的应用实例提供低延迟消息服务。互联网的故事还在进行,云计算规模化落地的时代悄然而来。 三、云计算时代的RocketMQ5.0 2015年,RocketMQ的首个云消息服务在阿里云上线,开启了大规模的云计算实践的序幕。同时RocketMQ也是业界第一个提供公有云服务的开源消息队列。 在大规模的云计算业务场景下,RocketMQ面临着全新的挑战与机遇。 多样性:它不再仅服务于某一家公司的内部业务,不再局限于互联网或金融企业,需要实现全行业、全场景的覆盖。 标准化:对于服务企业内部的自研消息队列而言,无需考虑协议或API的标准化。但是对于云消息服务而言,因为服务对象是外部企业客户,据信通院统计,80%以上的企业客户已经采纳开源技术和标准技术。因此,作为一款云消息服务,需要提供对业界的事实标准协议、接口、SDK的兼容,才能保证客户平滑上云,同时打消客户技术绑定的担忧。 云原生:云原生理念深入人心,消息队列要更好地帮助客户实现云原生应用架构,为业务降本提效。 新趋势:各种新技术的兴起,包括IoT、5G、边缘计算、事件驱动,还有事件流技术。面向技术的新趋势与多样化的业务需求,RocketMQ进行了自我进化,演进到5.0版本。 为了充分释放云的技术红利,RocketMQ5.0在技术架构上进行了云原生的演进。从客户端到服务端都进行了全方位的改造,更高弹性、可用性、更低成本。 客户端采用轻量SDK设计理念,将原来富客户端的逻辑下沉到Broker,满足现代化应用轻量化、Serverless的趋势。 Broker彻底进行弹性架构改造,分离RocketMQ Proxy与Store层,其中Proxy是完全无状态的计算节点,专注多协议、多领域场景覆盖,可以面向不同工作负载独立弹性,如物联网、微服务、大数据不同场景有不同的资源诉求。Store层则专注消息的高可用存储,包括副本复制、主备切换与云存储集成。同时对RocketMQ的Topic资源进行三层解耦,面向消息的Topic、面向流的Topic逻辑分片、面向底层存储的Topic物理分片,每一层都可以独立弹性。 在存储层引入了Leaderless的高可用架构,Store节点身份对等,Leaderless化,0外部依赖。多副本策略可定制,可用性+可靠性+成本灵活组合,面向多可用区、多region组建Geo高可用能力。 为了满足云时代多样化的用户需求,RocketMQ5.0从原来的互联网业务消息中间件扩展到"消息、事件、流"超融合处理平台,解锁更全面的能力。 在消息领域,全面拥抱云原生技术,更好的弹性架构与高可用能力。 在事件领域,支持CloudEvent规范,以事件为中心的产品新界面,助力客户建设跨业务、跨组织的数字化商业生态。 在流领域,流存储增强批量特性,大幅度提高数据吞吐量;新增逻辑队列能力,解耦逻辑资源与物理资源,在流场景也具备无缝伸缩能力;新增流数据库RSQLDB,提供实时事件流处理、流分析能力。 RocketMQ基于端云一体化架构实现了完整的物联网消息队列的能力,从原来的连接应用扩展到连接物联网设备。同时RocketMQ5.0也继续保持极简架构的原则,能够以最低的资源消耗、运维成本搭建服务,适合边缘计算。 除了的产品核心能力之外,RocketMQ5.0积极建设开源生态。 一方面是应用架构生态的建设,既有经典的开源项目、规范的集成,比如JMS、AMQP等,也有云原生技术生态的集成,比如CloudEvents、Dapr、Envoy。同时RocketMQ也会进一步发力数据架构生态,全链路集成大数据的摄入、数据存储、数据处理、数据分析组件,从离线大数据到实时大数据。 【活动】一键体验 RocketMQ 六大生产环境 免费试用+30秒一键体验,低门槛、快速、高效、易操作,带你了解“历经万亿级数据洪峰考验”的云消息队列RocketMQ! 点击阅读原文,立即参与活动! 活动推荐 阿里云基于 Apache RocketMQ 构建的企业级产品消息队列RocketMQ 5.0版现开启活动: 1、新用户首次购买包年包月,即可享受全系列 85折优惠! 了解活动详情:
作者:隆基
#技术探索 #云原生

2023年7月13日

RocketMQ 5.0 无状态实时性消费详解
背景 RocketMQ 5.0版本引入了Proxy模块、无状态pop消费机制和gRPC协议等创新功能,同时还推出了一种全新的客户端类型:SimpleConsumer。SimpleConsumer客户端采用了无状态的pop机制,彻底解决了在客户端发布消息、上下线时可能出现的负载均衡问题。然而,这种新机制也带来了一个新的挑战:当客户端数量较少且消息数量较少时,可能会出现消息消费延时的情况。。 在当前的消息产品中,消费普通使用了长轮询机制,即客户端向服务端发送一个超时时间相对较长的请求,该请求会一直挂起,除非队列中存在消息或该请求到达设定的长轮询时间。 然而,在引入Proxy之后,目前的长轮询机制出现了一个问题。客户端层面的长轮询和Proxy与Broker内部的长轮询之间互相耦合,也就是说,一次客户端对Proxy的长轮询只对应一次Proxy对Broker的长轮询。因此,在以下情况下会出现问题:当客户端数量较少且后端存在多个可用的Broker时,如果请求到达了没有消息的Broker,就会触发长轮询挂起逻辑。此时,即使另一台Broker存在消息,由于请求挂在了另一个Broker上,也无法拉取到消息。这导致客户端无法实时接收到消息,即false empty response。 这种情况可能导致以下现象:用户发送一条消息后,再次发起消费请求,但该请求却无法实时拉取到消息。这种情况对于消息传递的实时性和可靠性产生了不利影响。 AWS的文档里也有描述此等现象,他们的解决方案是通过查询是所有的后端服务,减少false empty response。 其他产品 在设计方案时,首先是需要目前存在的消息商业化产品是如何处理该问题的。 MNS采取了以下策略,主要是将长轮询时间切割为多个短轮询时间片,以尽可能覆盖所有的Broker。 首先,在长轮询时间内,会对后端的Broker进行多次请求。其次,当未超过短轮询配额时,优先使用短轮询消费请求来与Broker进行通信,否则将使用长轮询,其时间等于客户端的长轮询时间。此外,考虑到过多的短轮询可能会导致CPU和网络资源消耗过多的问题,因此在短轮询超过一定数量且剩余时间充足时,最后一次请求将转为长轮询。 然而,上述策略虽以尽可能轮询完所有的Broker为目标,但并不能解决所有问题。当轮询时间较短或Broker数量较多时,无法轮询完所有的Broker。即使时间足够充足的情况下,也有可能出现时间错位的情况,即在短轮询请求结束后,才有消息在该Broker上就绪,导致无法及时取回该消息。 解法 技术方案 首先,需要明确该问题的范围和条件。该问题只会在客户端数量较少且请求较少的情况下出现。当客户端数量较多且具备充足的请求能力时,该问题不会出现。因此,理想情况是设计一个自适应的方案,能够在客户端数量较多时不引入额外成本来解决该问题。 为了解决该问题,关键在于将前端的客户端长轮询和后端的Broker长轮询解耦,并赋予Proxy感知后端消息个数的能力,使其能够优先选择有消息的Broker,避免false empty response。 考虑到Pop消费本身的无状态属性,期望设计方案的逻辑与Pop一致,而不在代理中引入额外的状态来处理该问题。 另外,简洁性是非常重要的,因此期望该方案能够保持简单可靠,不引入过多的复杂性。 1. 为了解决该问题,本质上是要将前端的客户端长轮询和后端的Broker长轮询解耦开来,并赋予Proxy感知后端消息个数的能力,能够优先选择有消息的Broker,避免false empty response。 2. 由于Pop消费本身的无状态属性,因此期望该方案的设计逻辑和Pop一致,而不在Proxy引入额外的状态来处理这个事情。 3. Simplicity is ALL,因此期望这个方案简单可靠。 我们使用了NOTIFICATION,可以获取到后端是否有尚未消费的消息。拥有了上述后端消息情况的信息,就能够更加智能地指导Proxy侧的消息拉取。 通过重构NOTIFICATION,我们对其进行了一些改进,以更好地适应这个方案的要求。 pop with notify 一个客户端的请求可以被抽象为一个长轮询任务,该轮询任务由通知任务和请求任务组成。 通知任务的目的是获取Broker是否存在可消费的消息,对应的是Notification请求;而请求任务的目的是消费Broker上的消息,对应的是Pop请求。 首先,长轮询任务会执行一次Pop请求,以确保在消息积压的情况下能够高效处理。如果成功获取到消息,则会正常返回结果并结束任务。如果没有获取到消息,并且还有剩余的轮询时间,则会向每个Broker提交一个异步通知任务。 在任务通知返回时,如果不存在任何消息,长轮询任务将被标记为已完成状态。然而,如果相关的Broker存在消息,该结果将被添加到队列中,并且消费任务将被启动。该队列的目的在于缓存多个返回结果,以备将来的重试之需。对于单机代理而言,只要存在一个通知结果返回消息,Proxy即可进行消息拉取操作。然而,在实际的分布式环境中,可能会存在多个代理,因此即使通知结果返回消息存在,也不能保证客户端能够成功拉取消息。因此,该队列的设计旨在避免发生这种情况。 消费任务会从上述队列中获取结果,若无结果,则直接返回。这是因为只有在通知任务返回该Broker存在消息时,消费任务才会被触发。因此,若消费任务无法获取结果,可推断其他并发的消费任务已经处理了该消息。 消费任务从队列获取到结果后,会进行加锁,以确保一个长轮询任务只有一个正在进行的消费任务,以避免额外的未被处理的消息。 如果获取到消息或长轮询时间结束,该任务会被标记完成并返回结果。但如果没有获取到消息(可能是其他客户端的并发操作),则会继续发起该路由所对应的异步通知任务,并尝试进行消费。 自适应切换 考虑到当请求较多时,无需采用pop with notify机制,可使用原先的pop长轮询broker方案,但是需要考虑的是,如何在两者之间进行自适应切换。目前是基于当前Proxy统计的pop请求数做判断,当请求数少于某一值时,则认为当前请求较少,使用pop with notify;反之则使用pop长轮询。 由于上述方案基于的均为单机视角,因此当消费请求在proxy侧不均衡时,可能会导致判断条件结果有所偏差。 Metric 为了之后进一步调优长轮询和观察长轮询的效果,我们设计了一组metric指标,来记录并观测实时长轮询的表现和损耗。 1. 客户端发起的长轮询次数 (is_long_polling) 2. pop with notify次数 (通过现有rpc metric统计) 3. 首次pop请求命中消息次数 (未触发notify) (is_short_polling_hit) 总结 通过如上方案,我们成功设计了一套基于无状态消费方式的实时消费方案,在做到客户端无状态消费的同时,还能够避免false empty response,保证消费的实时性,同时,相较于原先PushConsumer的长轮询方案,能够大量减少用户侧无效请求数量,降低网络开销, 产品侧 需明确长轮询和短轮询的区分,可以参考AWS的定义,当轮询时间大于0时,长轮询生效。 且需明确一个长轮询最小时间,因为长轮询时间过小时无意义,AWS的最小值采取了1s,我们是否需要follow,还是采取一个更大的值。 活动推荐 阿里云基于 Apache RocketMQ 构建的企业级产品消息队列RocketMQ 5.0版现开启活动: 1、新用户首次购买包年包月,即可享受全系列 85折优惠! 了解活动详情:
#技术探索 #功能特性 #云原生

2023年4月11日

RocketMQ 多级存储设计与实现
设计总览 RocketMQ 多级存储旨在不影响热数据读写的前提下将数据卸载到其他存储介质中,适用于两种场景: 1. 冷热数据分离:RocketMQ 新近产生的消息会缓存在 page cache 中,我们称之为热数据;当缓存超过了内存的容量就会有热数据被换出成为冷数据。如果有少许消费者尝试消费冷数据就会从硬盘中重新加载冷数据到 page cache,这会导致读写 IO 竞争并挤压 page cache 的空间。而将冷数据的读取链路切换为多级存储就可以避免这个问题; 2. 延长消息保留时间:将消息卸载到更大更便宜的存储介质中,可以用较低的成本实现更长的消息保存时间。同时多级存储支持为 topic 指定不同的消息保留时间,可以根据业务需要灵活配置消息 TTL。 RocketMQ 多级存储对比 Kafka 和 Pulsar 的实现最大的不同是我们使用准实时的方式上传消息,而不是等一个 CommitLog 写满后再上传,主要基于以下几点考虑: 1. 均摊成本:RocketMQ 多级存储需要将全局 CommitLog 转换为 topic 维度并重新构建消息索引,一次性处理整个 CommitLog 文件会带来性能毛刺; 2. 对小规格实例更友好:小规格实例往往配置较小的内存,这意味着热数据会更快换出成为冷数据,等待 CommitLog 写满再上传本身就有冷读风险。采取准实时上传的方式既能规避消息上传时的冷读风险,又能尽快使得冷数据可以从多级存储读取。 Quick Start 多级存储在设计上希望降低用户心智负担:用户无需变更客户端就能实现无感切换冷热数据读写链路,通过简单的修改服务端配置即可具备多级存储的能力,只需以下两步: 1. 修改 Broker 配置,指定使用 org.apache.rocketmq.tieredstore.TieredMessageStore 作为 messageStorePlugIn 2. 配置你想使用的储存介质,以卸载消息到其他硬盘为例:配置 tieredBackendServiceProvider 为 org.apache.rocketmq.tieredstore.provider.posix.PosixFileSegment,同时指定新储存的文件路径:tieredStoreFilepath 可选项:支持修改 tieredMetadataServiceProvider 切换元数据存储的实现,默认是基于 json 的文件存储 更多使用说明和配置项可以在 GitHub 上查看多级存储的 技术架构 architecture 接入层:TieredMessageStore/TieredDispatcher/TieredMessageFetcher 接入层实现 MessageStore 中的部分读写接口,并为他们增加了异步语意。TieredDispatcher 和 TieredMessageFetcher 分别实现了多级存储的上传/下载逻辑,相比于底层接口这里做了较多的性能优化:包括使用独立的线程池,避免慢 IO 阻塞访问热数据;使用预读缓存优化性能等。 容器层:TieredCommitLog/TieredConsumeQueue/TieredIndexFile/TieredFileQueue 容器层实现了和 DefaultMessageStore 类似的逻辑文件抽象,同样将文件划分为 CommitLog、ConsumeQueue、IndexFile,并且每种逻辑文件类型都通过 FileQueue 持有底层物理文件的引用。有所不同的是多级存储的 CommitLog 改为 queue 维度。 驱动层:TieredFileSegment 驱动层负责维护逻辑文件到物理文件的映射,通过实现 TieredStoreProvider 对接底层文件系统读写接口(Posix、S3、OSS、MinIO 等)。目前提供了 PosixFileSegment 的实现,可以将数据转移到其他硬盘或通过 fuse 挂载的对象存储上。 消息上传 RocketMQ 多级存储的消息上传是由 dispatch 机制触发的:初始化多级存储时会将 TieredDispatcher 注册为 CommitLog 的 dispacher。这样每当有消息发送到 Broker 会调用 TieredDispatcher 进行消息分发,TieredDispatcher 将该消息写入到 upload buffer 后立即返回成功。整个 dispatch 流程中不会有任何阻塞逻辑,确保不会影响本地 ConsumeQueue 的构建。 TieredDispatcher TieredDispatcher 写入 upload buffer 的内容仅为消息的引用,不会将消息的 body 读入内存。因为多级储存以 queue 维度构建 CommitLog,此时需要重新生成 commitLog offset 字段 upload buffer 触发 upload buffer 上传时读取到每条消息的 commitLog offset 字段时采用拼接的方式将新的 offset 嵌入到原消息中 上传进度控制 每个队列都会有两个关键位点控制上传进度: 1. dispatch offset:已经写入缓存但是未上传的消息位点 2. commit offset:已上传的消息位点 upload progress 类比消费者,dispatch offset 相当于拉取消息的位点,commit offset 相当于确认消费的位点。commit offset 到 dispatch offset 之间的部分相当于已拉取未消费的消息 消息读取 TieredMessageStore 实现了 MessageStore 中的消息读取相关接口,通过请求中的逻辑位点(queue offset)判断是否从多级存储中读取消息,根据配置(tieredStorageLevel)有四种策略: DISABLE:禁止从多级存储中读取消息; NOT_IN_DISK:不在 DefaultMessageStore 中的消息从多级存储中读取; NOT_IN_MEM:不在 page cache 中的消息即冷数据从多级存储读取; FORCE:强制所有消息从多级存储中读取,目前仅供测试使用。 ${a} 需要从多级存储中读取的消息会交由 TieredMessageFetcher 处理:首先校验参数是否合法,然后按照逻辑位点(queue offset)发起拉取请求。TieredConsumeQueue/TieredCommitLog 将逻辑位点换算为对应文件的物理位点从 TieredFileSegment 读取消息。 ${b} TieredFileSegment 维护每个储存在文件系统中的物理文件位点,并通过为不同存储介质实现的接口从中读取所需的数据。 ${c} 预读缓存 TieredMessageFetcher 读取消息时会预读一部分消息供下次使用,这些消息暂存在预读缓存中 ${d} 预读缓存的设计参考了 TCP Tahoe 拥塞控制算法,每次预读的消息量类似拥塞窗口采用加法增、乘法减的机制控制: 加法增:从最小窗口开始,每次增加等同于客户端 batchSize 的消息量。 乘法减:当缓存的消息超过了缓存过期时间仍未被全部拉取,在清理缓存的同时会将下次预读消息量减半。 预读缓存支持在读取消息量较大时分片并发请求,以取得更大带宽和更小的延迟。 某个 topic 消息的预读缓存由消费这个 topic 的所有 group 共享,缓存失效策略为: 1. 所有订阅这个 topic 的 group 都访问了缓存 2. 到达缓存过期时间 故障恢复 上文中我们介绍上传进度由 commit offset 和 dispatch offset 控制。多级存储会为每个 topic、queue、fileSegment 创建元数据并持久化这两种位点。当 Broker 重启后会从元数据中恢复,继续从 commit offset 开始上传消息,之前缓存的消息会重新上传并不会丢失。 开发计划 面向云原生的存储系统要最大化利用云上存储的价值,而对象存储正是云计算红利的体现。 RocketMQ 多级存储希望一方面利用对象存储低成本的优势延长消息存储时间、拓展数据的价值;另一方面利用其共享存储的特性在多副本架构中兼得成本和数据可靠性,以及未来向 Serverless 架构演进。 tag 过滤 多级存储拉取消息时没有计算消息的 tag 是否匹配,tag 过滤交给客户端处理。这样会带来额外的网络开销,计划后续在服务端增加 tag 过滤能力。 广播消费以及多个消费进度不同的消费者 预读缓存失效需要所有订阅这个 topic 的 group 都访问了缓存,这在多个 group 消费进度不一致的情况下很难触发,导致无用的消息在缓存中堆积。 需要计算出每个 group 的消费 qps 来估算某个 group 能否在缓存失效前用上缓存的消息。如果缓存的消息预期在失效前都不会被再次访问,那么它应该被立即过期。相应的对于广播消费,消息的过期策略应被优化为所有 Client 都读取这条消息后才失效。 和高可用架构的融合 目前主要面临以下三个问题: 1. 元数据同步:如何可靠的在多个节点间同步元数据,slave 晋升时如何校准和补全缺失的元数据; 2. 禁止上传超过 confirm offset 的消息:为了避免消息回退,上传的最大 offset 不能超过 confirm offset; 3. slave 晋升时快速启动多级存储:只有 master 节点具有写权限,在 slave 节点晋升后需要快速拉起多级存储断点续传。 活动推荐 阿里云基于 Apache RocketMQ 构建的企业级产品消息队列RocketMQ 5.0版现开启活动: 1、新用户首次购买包年包月,即可享受全系列 85折优惠! 了解活动详情:
作者:张森泽
#技术探索 #云原生

2022年12月14日

事件总线 + 函数计算构建云上最佳事件驱动架构应用
距离阿里云事件总线(EventBridge)和 Serverless 函数计算(Function Compute,FC)宣布全面深度集成已经过去一年,站在系统元数据互通,产品深度集成的肩膀上,这一年我们又走过了哪些历程?从事件总线到事件流,从基于 CloudEvents 的事件总线触发到更具个性化的事件流触发,函数计算已成为事件总线生态不可或缺的重要组成部分,承载了 EventBridge 系统架构中越来越多的角色,事件流基础架构的函数 Transform,基于函数计算的多种下游 Sink Connector 投递目标支持,函数作为 EventBridge 端点 API Destination;基于事件总线统一,标准的事件通道能力,和基于函数计算敏捷、轻量、弹性的计算能力,我们将又一次起航探索云上事件驱动架构的最佳实践。 今天的主题围绕事件总线+函数计算,构建云上最佳事件驱动架构应用。希望通过今天的分享,能够帮助大家深入理解 Serverless 函数计算、EventBridge 事件总线对于构建云上事件驱动架构应用的价值和背后的逻辑、 为什么函数计算是云上事件驱动服务最佳实践?为什么我们如此需要事件总线服务?伴随着这些谜题的解开,最后,让我们一起了解应用于实际生产的一些 Serverless 事件驱动客户案例。 事件驱动架构的本质 Back to the Nature of EventDriven 大家可能会疑惑,事件驱动家喻户晓,为什么我们又要重新讨论事件驱动呢?我想这也正是我们需要讨论它的原因,回归本质,重新起航;事件驱动可能是一个比较宽泛的概念,而本文聚焦事件驱动架构的讨论,事件驱动架构作为一种软件设计模式,的确不是一个新的概念,伴随着计算机软件架构的演进,它已经存在了一段很久的时间,大家对它的讨论也从未停止过,当我们需要重新讨论一个已经存在的概念的时候,我想我们有必要重新回到它最开始的定义,一起探索那些本质的东西,重新认识它。 上面的这些内容是我从相关的一些资料上摘录的关于事件驱动的一些描述,“abstract”,“simple”,“asynchronous”,“messagedriven”这些具有代表性的词汇很好的给予事件驱动一个宏观的描述;从事件驱动的抽象概念,到它简洁的架构,以及事件驱动架构要达成的目的,和它在实际的系统架构中所展现的形态。 事件驱动架构基本概念及形态 在了解了关于事件驱动架构的一些基本描述之后,我们需要进一步明确事件驱动架构所涉及的一些基本概念和架构形态。根据维基百科描述,事件驱动架构涉及的核心概念如下所示: 围绕事件的流转,根据事件驱动架构的概念和基本形态,主要涉及以下四个核心部分: Event Producer:负责产生事件,并将产生的事件投递到事件通道; Event Channel:负责接收事件,并将接收的事件持久化存储,投递给订阅该事件的后端处理引擎; Event Processing Engine:负责对于订阅的事件做出响应和处理,根据事件更新系统状态; Downstream eventdriven activity:事件处理完成之后,对于事件处理响应的一种展示; 事件驱动架构要达成的目的 了解了事件驱动架构的基本形态,架构中事件通道的引入,解耦了事件生产和事件处理这两个最基本的系统角色,那么这样的架构模型所要达成的最终目的到底是什么? 系统架构松耦合 事件生产者与事件订阅者在逻辑上是分开的。事件的生成与使用的分离意味着服务具有互操作性,但可以独立扩缩、更新和部署。 只面向事件的松散耦合可以减少系统依赖项,并允许您以不同的语言和框架实现服务。您无需更改任何一个服务的逻辑,即可添加或移除事件生成方和接收方。您无需编写自定义代码来轮询、过滤和路由事件。 系统的可伸缩性 基于事件驱动架构的松耦合特性,意味着可以独立对事件生产者,事件通道服务,以及事件处理引擎进行独立的扩缩容;尤其对于后端事件处理引擎,可以根据消息处理响应 SLA 和后端资源供给进行弹性扩缩容;同时可以基于事件粒度构建不同规格的后端处理服务,实现更细粒度的系统弹性伸缩。 系统的可扩展性 系统的可扩展性,主要表现在当系统需要增加新的功能,如何快速的基于现有系统架构快速构建支持新的业务逻辑,在事件驱动架构应用中,围绕事件粒度的处理模式,能够天然快速支持增加新的基于事件的数据流抽象;当系统中增加新的事件类型的时候,无需调整变更发布整个系统,只需要关注需要订阅的事件进行事件处理逻辑的开发和部署即可,也可以基于原来的系统做很少的代码变更即可实现,也可以在业务初期通过独立的服务定于指定事件完成特定的业务逻辑支持。 为什么函数计算是云上事件驱动服务最佳实践? 在讨论完事件驱动架构基本模型之后,我想关于事件驱动的概念,形态我们有了统一的认识和理解,接下来我们进入议题的第二个部分,为什么函数计算是云上事件驱动服务最佳实践? 函数计算简介 函数计算是一款基于事件驱动的全托管计算服务,相关的产品细节可以见官网介绍。作为一款通用的事件驱动型计算服务,接下来我会从三个方面进行详细的介绍。 编程范式 使用函数计算,用户无需采购与管理服务器等基础设施,只需编写并上传代码。函数计算为你准备好计算资源,弹性地、可靠地运行任务,并提供日志查询、性能监控和报警等开箱即用功能,编程范式带来开发的敏捷性。按照函数粒度进行独立的功能单元开发,快速调试,快速的部署上线,省去了大量资源购买,环境搭建的运维工作;同时函数计算是一个事件驱动的模型,事件驱动,意味着用户不需要关注服务产品数据传递的问题,省去了在编写代码中涉及的大量服务访问连接的逻辑;“事件驱动” + “函数粒度开发” + “免服务器运维”等几个维度特征帮助函数计算支撑“聚焦业务逻辑敏捷开发”的底层逻辑。 计算模型 除了开发模式带来的研发效能提升之外,函数计算提供非常细粒度的计算资源和毫秒级计费模型,支撑按需计算,按量收费;能够支持按用户的请求,根据用户流量的模型为计算付费;当然按用户请求付费存在技术上巨大的挑战,要求函数计算实例的启动小于用户的 RT 要求,冷启动性能尤为重要,这时候极致弹性成为了 Serverless 按需付费,业务降本的底层技术支撑。函数计算通过“极致弹性” + “按需付费”的模型帮助 Serverless 函数计算实现真正的按需计算逻辑。 事件驱动 在基于云的开发环境,云产品承载的服务相对内聚,各自扮演着分布式系统架构中的各个重要角色,云产品之间的事件触发机制能够帮助客户更好的基于多个云产品构建自己的业务系统;否则在云产品之间 Watch 事件是非常复杂,开发代价非常昂贵的一件事;除了产品连接带来的开发效率之外,当用户订阅某个事件,并提供处理逻辑的时候,客户已经潜在的过滤掉了不需要处理的事件请求,事件驱动意味着每一次的事件触发请求都是一次完全有效的计算。 函数计算对于事件驱动架构的价值 为什么函数计算是云上最佳的事件驱动架构服务?函数计算对于事件驱动架构的核心价值到底是什么?事件驱动架构一直存在,在没有函数计算的时候,同样也有事件驱动架构,在微服务的时候也同样有事件驱动架构。如今,当我们重新再来讨论事件驱动架构的时候,到底是什么发生了变化,有哪些本质的区别?在整个事件驱动架构中,函数计算最大的价值在于帮助构建 “Event Processing Engine” 这个角色,我想主要是以下两个方面发生了本质的变化: 系统可扩展性价值 开发模式发生了本质的变化:函数计算提供的框架能力及编程模型,最大化的消除了客户业务逻辑之外的处理内容,极大的加速了客户业务开发,同时基于这样这样的开发模式,用户对于新增事件处理逻辑能够在最短的时间完成处理并上线,细粒度,专注业务的敏捷开发模式能够加速业务快速上线。 系统可伸缩性价值 计算模式发生了本质的变化:基于事件驱动架构事件粒度的处理逻辑和函数计算更细粒度力度计算弹性能力,能够从多个维度实现 “Event Processing Engine” 组件的弹性能力, 这我想这也是函数计算对于事件驱动架构的一个最核心价值。 为什么我们如此需要事件总线服务? 构建云上事件驱动架构挑战 函数计算以其轻量,快捷,能够利用事件驱动的方式与其他云产品进行联动的特点, 成为很多客户利用事件驱动架构构建业务系统的首选,随着业务及客户需求的不断增加,客户对于函数计算和更多云产品及服务的连接需求变得越来越多,同时对于其他云产品的客户而言, 也希望能够利用 Serverless 函数计算的特点帮助处理一些系统任务和事件。 尽管函数计算和云上的众多云产品进行了集成,提供了一些开箱即用的事件触发能力,那么我们为什么还需要事件总线服务来构建事件驱动应用架构呢?围绕函数计算构建事件驱动架构生态的过程中,我们面临主要来自三个方面的挑战。面对这些挑战,基于函数计算和事件总线帮助云上客户构建完备的事件驱动架构生态迫在眉睫。 事件源多样性挑战 事件驱动作为函数计算产品核心竞争力,打通函数计算和其它云产品,以及用户自定义应用,SaaS 服务的连通成为函数计算生态集成的迫切需求,但系统集成,生态建设从来都不是一件容易的事情。函数计算系统在和 EventBridge 集成之前,已经和 OSS,SLS 等用户典型场景的云产品进行了集成,也和阿里云的其它大概十多款产品进行了集成,不同系统具有不同的事件格式,不同系统的注册通知机制也各不相同,以及上游不同系统的失败处理机制也各不相同;部分系统支持同步的调用方式,部分系统支持异步的调用方式,调用方式的差异主要取决于上游系统在接入函数计算的时候当时面临的产品业务场景,对于新的产品能力和业务场景的扩展支持,在当时并未有太多的考虑。随着和更多云产品的集成,集成的投入,集成的困难度和底层数据管理难度越来越大。面对多种事件源集成的客观困难,函数计算急需提高和其他云产品的集成效率。 授权复杂及安全隐患 除此之外, 函数计算希望提升用户体验,保证用户只关心事件的处理;同时希望能够在面对大量的云产品时保证系统授权层面的复杂度。用户在使用事件触发的时候, 需要了解不同产品接入函数计算的权限要求,针对不同的产品需要提供不同的授权策略,对于客户使用函数计算带来了非常大的困难,为了加速产品接入,大量用户经常使用FullAcees权限,造成较大产品安全隐患, 和其它云产品的集成急需统一的授权界面,统一用户体验。 通用能力难以沉淀 面对上游不同的事件源, 如何更好的投递事件、更好的消费事件?如何进行事件的错误处理?函数计算调用方式如何选择?以及函数计算后端错误 Backpressure 能力的反馈、重试策略和上游系统参数设置、触发器数量的限制等问题获成为函数计算事件触发不得不面对的问题。为了更好的服务客户,提供可靠的消费处理能力,函数计算希望能够有一个统一的接入层,基于统一的接入层进行消费能力和流控能力的建设。通过沉淀在这样一个标准的层面,在保证调用灵活性的同时,提供可靠的服务质量。 事件总线简介 阿里云事件总线(EventBridge) 是一种无服务器事件总线,支持将用户的应用程序、第三方软件即服务 (SaaS)数据和阿里云服务的数据通过事件的方式轻松的连接到一起,这里汇聚了来自云产品及 SaaS 服务的丰富事件; 总线模式(EventBus) 从整个架构来看,EventBridge 通过事件总线,事件规则将事件源和事件目标进行连接。首先,让我们快速普及下 EventBridge 架构中涉及的几个核心概念: 事件:状态变化的记录; 事件源:事件的来源,事件的产生者,产生事件的系统和服务, 事件源生产事件并将其发布到事件总线; 事件总线:负责接收来自事件源的事件;EventBridge 支持两种类型的事件总线: 云服务专用事件总线:无需创建且不可修改的内置事件总线,用于接收您的阿里云官方事件源的事件。 自定义事件总线:标准存储态总线,用于接收自定义应用或存量消息数据的事件,一般事件驱动可选该总线。 事件规则:用于过滤,转化事件,帮助更好的投递事件; 事件目标:事件的消费者,负责具体事件的处理。 通过上面的流程,完成了事件的产生,事件的投递,事件的处理整个过程。当然事件并不是一个新的概念,事件驱动架构也不是一个新的概念,事件在我们的系统中无处不在,事件驱动架构同样伴随着整个计算机的架构演进,不断地被讨论。对于 EventBridge,采用云原生事件标准 CloudEvents 来描述事件;带来事件的标准化,这样的标准化和事件标准的开放性带来一个最显著的优势:接入的标准化,无论是对于事件源还是事件目标。 事件流模式(EventStreaming) 消息产品凭借其异步解耦、削峰填谷的特点,成为了互联网分布式架构的必要组成部分,Serverless 函数计算有着与其完全吻合的应用场景,针对消息产品生态集成,函数计算在架构层面做了专门的建设,基于 EventBridge 产品提供的 EventStreaming 通道能力建设了通用的消息消费服务 Poller Service,基于该架构对用户提供了 RocketMQ,Kafka,RabbitMQ,MNS 等多个消息类型触发能力。 将消费的逻辑服务化,从业务逻辑中剥离由平台提供,消费逻辑和处理逻辑的分离,将传统架构的消息拉模型转化成 Serverless 化的事件驱动推模型,能够支撑由函数计算承载消息处理的计算逻辑 ,实现消息处理的 Serverless 化。基于这样的架构,能够帮助客户解决消息客户端的集成连接问题,简化消息处理逻辑的实现,同时对于波峰波谷的业务模型,结合函数计算提供细粒度的计算弹性能力,能够实现资源的动态扩容,降低用户成本。 事件总线对于事件驱动架构的价值 简化统一事件源接入 沉淀通用事件通道能力 提升优化用户集成体验 利用函数计算提供的 HTTP 函数 URL 能力,结合事件总线端点 API 能力,能够快速的帮助客户进行系统扩展和集成。 客户场景案例分享 总线模式 + 函数计算用户案例 利用 ActionTrail 事件触发函数进行多账号审计管理 利用 OSS 文件上传事件触发函数扩容 ACK  集群资源 利用 OSS 文件上传执行 Terraform 文件并访问外部 API 做结果通知 事件流模式 + 函数计算用户案例 利用函数计算细粒度资源弹性特征,结合业务波峰波谷的特点,实现快速的消息清洗和处理。 事件流触发函数计算处理业务消息 事件流触发函数计算进行简单 ETL 数据同步 事件流触发函数进行简单 ETL 数据清洗入库 函数异步+事件流触发函数构建电商运营通知系统 在购物车加购,商品变更通知场景,利用函数计算异步系统(内部自带 Queue 能力),触发大量运营通知,利用函数异步的 Destination 能力将运营通知结果写入 MQ,然后利用事件流能力对 MQ 数据进行再次处理,写入HBase数据库中。 活动推荐 阿里云基于 Apache RocketMQ 构建的企业级产品消息队列RocketMQ 5.0版现开启活动: 1、新用户首次购买包年包月,即可享受全系列 85折优惠! 了解活动详情:
作者:世如
#行业实践 #事件驱动架构 #云原生

2022年10月12日

RocketMQ 5.0 API 与 SDK 的演进
RocketMQ 5.0 SDK 采用了全新的 API,使用 gRPC 作为通信层的实现,并在可观测性上做了很大幅度的提升。 全新统一的 API 此处的 API 并不单单只是接口上的定义,同时也规定了各个接口不同的方法和行为,明确了整个消息模型。 RocketMQ 过去的 API 从第一版开始,至今已经过了很长时间,长期依赖是一个缺乏变革的状态,对于一些中途打算废弃或者变更的 API 也没有进行后续的迭代。此外,接口的定义也不够清晰。因此,RocketMQ 希望在 5.0 中能够建立一个统一的规范,精简整个 API,通过引入 builder 模式来引入更多的不变性,同时做好异常管理,给开发者和用户一个更加清爽的面貌。 目前 C++ 和 Java 已经进行了 5.0 API 的定义与实现,更多语言的支持也陆续在路上了。我们也欢迎更多的开发者可以参与到社区的工作中来。这里给出 5.0 客户端的仓库链接: 除了在上述接口上做的一些修改之外, RocketMQ 5.0 还规定了四种新的不同的客户端类型,即 Producer/Push Consumer/Simple Consumer/Pull Consumer。 其中 Pull Consumer 还在开发中;Producer 主要还是做了接口裁剪,规范了异常管理。在功能上其实并没有做一些颠覆性的改变。Push Consumer 也是比较类似的;Simple consumer 将更多的权利将下发给用户,是一种用户可以主动控制消息接收与处理过程的消费者,特别的,5.0 的 SDK 中,Push Consumer 和 Simple Consumer 都采用 RocketMQ 的 pop 机制进行实现,一些社区的同学可能已经熟悉了。 如果用户并不一定想控制或者关心整个消息的接收过程,只在乎消息的消费过程的话,这个时候 Push Consumer 可能是一个更好的选择。 RocketMQ 5.0 定义了四种不同的消息类型。过去的开源版本中其实我们并没有去突出消息类型这样一个概念,后续出于维护及运维方面的需要以及模型定义的完备,才让今天的 5.0 有了消息类型的这样一个概念。 1、NORMAL:普通消息。 2、FIFO:满足先入先出的语义。用户可以通过定义 message group 来控制消息间的消费顺序。例如图中的 fruit 这个 topic 下,可以定义不同的 message group,在 apple 这个 message group 下,会按照发送顺序决定消息被消费的顺序,并且不同的 message group 之间不会互相干扰。 3、TRANSACTIONAL:可以将一条或多条消息包成一个事务,最终用户可以根据自己的业务结果选择提交或者回滚。 4、DELAY:用户可以自主地设置消息的定时时间,相比之前开源版本仅允许用户设置定时/延迟级别,5.0 的实现中还讲允许用户设置更精确的时间戳。 以上四种消息是互斥的,我们会在 topic 的元数据去标识它的类型。实际在消息发送的时候如果如果出现了尝试发送的消息类型与 topic 类型不匹配的情况,也会做一些限制。 实现 RocketMQ 5.0 在客户端的启动过程中提前进行了更多的准备工作。比如用户提前设置要发送消息的 topic 时,Producer 会在启动过程中尝试获取对应 topic 的路由。在过去的客户端实现中,在针对于某个 topic 第一次发送消息时,需要先获取路由,这里就会有一个类似冷启动的过程。 提前获取 Topic 的路由信息有两点好处: 1. 不阻塞后面的发送,让消息的发送仅仅触发发送这一个动作。 2. 错误前置,比如用户要往一个不存在 Topic 发送消息时,因为路由的获取参与到整个客户端的启动过程,获取路由不成功,那整个客户端启动可能就会失败,用户也是拿不到对应的 Producer 对象的。 类似的,Consumer 的启动也会有这样的一个过程。 除此之外,我们在客户端和服务端之间增加了一个 Telemetry 的部分,它会在客户端和服务端之间建立起了一个进行双向数据通讯的通道,客户端和服务端会在这个过程中沟通配置,比如服务端可以实现对客户端配置的下发,更好地管理客户端。此外,Telemetry 也可以将本地配置主动上报给服务端,让服务端也可以对客户端的设置有更好的了解。Telemetry 通道也会在客户端启动时尝试建立,如果这个通道没有建立成功,也会影响客户端的启动。 总的来说,客户端的启动过程会尽可能将所有准备工作做好。同时在客户端和服务端之间建立 Telemetry 这样一个通讯通道。 客户端内部存在一些周期性的任务,比如路由的定时更新以及客户端往服务端发送心跳等。对于上文中提到的 Telemetry 过程中,客户端的配置上报也是周期性的。 Producer 在 RocketMQ 5.0 中的具体工作流程 消息在发送时,会检查是否已经获取对应 topic 的路由信息。如果已经获取,则尝试在路由中选取队列,随即查看要发送的消息的类型是否与 topic 类型匹配,如果匹配,则进行消息发送。如果发送成功,则返回;否则,判断当前重试次数是否超出用户设置的上限,如果超出,则返回失败;否则轮转到下一个队列,然后对新的队列进行重试直到消费次数超出上线。而如果启动过程中没有提前获取路由,那么消息发送时依然会先尝试获取路由,然后再进行下一步操作。 另外一点相对于老客户端较大的改变在于,客户端从底层 RPC 交互到上层的业务逻辑全部采用异步实现。Producer 依然会提供一个同步发送接口和异步发送接口,但同步的方法也是使用异步来实现,整个逻辑非常统一和清爽。 Push Consumer 分为两部分,消息的接收和消费。 消息接收流程为:客户端需要不断地从服务端拉取消息,并将消息缓存。Push Consumer 会将消息先缓存到客户端的本地,再进行消费,因此它会判断客户端本地的 Cache 是否已满,如果已满,则隔一段时间再判断,直到消息被客户端消费,Cache 尚有余量时再进行消息拉取。为了避免出现一些内存问题,Cache 的大小也是被严格限制的。 消息消费过程分为两个类型,顺序类型和非顺序类型。 其中非顺序类型即并发消费。消费者会先从 Cache 中获取消息,然后尝试消费消息,消费后再将消息从 Cache 中移除。消息消费成功时,会尝试将消息 ACK ,消费流程结束;如果消费失败,则尝试修改消息的可见时间,即决定下一次什么时候可见。 顺序消费指对于同一个 Group 的消息,最终消费时一定是前一条消息被消费过并且得到确认后,后面的消息才能够继续消费。而消费过程与非顺序消费类似,首先尝试从 Cache 中拉取消息,如果消费成功,则将消息 ACK。ACK 成功后,将其从 Cache 中移除。特别地,如果消费失败,会 suspend 一段时间,然后继续尝试对消息进行消费。此时会判断消费次数是否超限,如果超限,则会尝试将消息放入死信队列中。 相对于非顺序消费,顺序消费更复杂,因为其需要保证前一个消息消费成功后才能对后面的消息进行消费。顺序消费的消费逻辑是基于 message group 隔离的。message group 会在发送时做哈希,从而保证 message group 的消息最终会落在一个队列上,顺序消费模式本质上保证队列内部消费的顺序。 此外,因为不同 message group 的顺序消息最终可能会映射到同一个队列上,这可能会导致不同的 message group 之间的消费形成阻塞,因此服务端未来会实现一个虚拟队列,让不同的 message group 映射到客户端的虚拟队列,保证他们之间没有任何阻塞,从而加速数据消息的消费过程。 对于 Simple Consumer,用户可以主动控制消息接收和确认的流程。比如用户收到消息后,可以根据业务决定是否过一段时间再消费该消息,或者不需要再收到该消息。消费成功后将消息 ACK 掉,如果失败则主动修改可见时间,选择该消息下一次什么时候可见,即由用户自发地控制整个过程。 可观测性 Shaded Logback 因为历史原因,RocketMQ 的老客户端并不是面向 SLF4J 进行编程的,而是面向 logback 的。这么做的目的其实是为了方便快捷地获取日志,不需要让用户自己去手动配置。 RocketMQ 中专门有一个 logging 模块是负责日志部分的,像用户自己使用了 logback ,RocketMQ SDK 如果也直接去使用 logback,两者就会产生各种各样的冲突,这个 logging 模块就是用来保证这一层隔离性的。 但是 logging 模块本身的实现并不是很优雅,也带来了一定的维护成本。因此我们采用了 shade logback 的方式来达到上文提到的隔离性。shaded logback 不仅能够避免用户的 logback 与 RocketMQ 自己的 logback 冲突,还能保持较好的可维护性,将来要想在日志上去做一些修改,也来得容易的多。 具体来说,用户的 logback 会采用 logback.xml 的配置文件,通过 shade logback, RocketMQ 5.0 的客户端会使用 rocketmq.logback.xml 的配置文件,因此在配置部分就已经完全隔离了,同时在 shade 的过程中,还对原生 logback 中使用到的一些环境变量和系统变量也进行了修改,这样就保证了两者的彻底隔离。 另外,使用 shadeed logback 之后,RocketMQ 5.0 客户端中的日志部分就全都是面向 SLF4J 来进行编程的了,这样一来,如果我们未来想让用户自己去完全控制日志的话,提供一个去除 logback 的 SDK 就可以了,非常方便。 Trace 5.0 的消息轨迹基于 OpenTelemetry 模型进行定义与实现,消息发送或接收消息的流程被定义为一个个独立的 span ,这一套 span 规范参照了 OpenTelemetry 关于 Messaging 的定义。图中这里 Process P 表示 Producer ,Process C 表示 Consumer。消息的全生命周期,从发送到接收到消费,就可以具象化为这样一个个的 span。 比如,针对 Push Consumer 而言,先会有一个 receive 的 span 来表示从服务端获取消息的过程,收到消息后到会先等待消息被处理,这个也就是 await span 表示的过程,消息被处理则对应图中的 process span,消息消费结束之后,向服务端反馈消息处理结果也会有专门的 span 进行描述。 我们通过 parent 和 link 来讲所有的这些 span 关联起来,这样通过一条消息的任意一个 span,就可以获得这条消息全生命周期的所有 span。 不仅如此,用户还将允许可以设置一个 span context 与自己的业务链路进行关联,将 RocketMQ 5.0 的消息轨迹本身嵌入进自己的全链路可观测系统中去。 Metrics Tracing 相对来说成本是比较高的,因为一条消息从发送到接收,可能会有很多流程,这就伴随着很多的 span,这就导致相对来说,tracing 数据的存储查询成本相对来说比较高。我们希望诊断整个 SDK 的健康状况,同时又不希望收集太多的 tracing 信息提高成本,此时提供一份 metrics 数据就能比较好地满足我们的需求。 在 SDK 的 metrics 中我们新增了诸多指标,包括不限于 Producer 中消息发送延时,Push Consumer 中消息的消费耗时和消息缓存量,可以帮助用户和运维者更快更好地发现异常。 5.0 中 SDK 的 metrics 也是基于 OpenTelemetry 进行实现的。以 Java程序为例,OpenTelemetry 对于 Java 的实现本身提供了一个 agent,agent 在运行时会打点采集 SDK 的一些 tracing/metrics 信息,并将它上报到对应的 metric collector 中,这种通过 agent 来降低无侵入式数据采集的方式被称之为 automatic instrumentation,而手动在代码中实现打点采集的方式则被称之 manual instrumentation。对于 metrics 我们目前还是采用 manual instrumentation 的方式来进行数据的采集和上报的。服务端会告知客户端对应的 collector 的地址,然后客户端将 Metrics 数据上传到对应的 collector 当中去。 _作者介绍:_ _艾阳坤,Apache RocketMQ 5.0 Java SDK 作者,CNCF Envoy Contributor,CNCF OpenTelemetry Contributor,阿里云智能高级开发工程师。_ 活动推荐 阿里云基于 Apache RocketMQ 构建的企业级产品消息队列RocketMQ 5.0版现开启活动: 1、新用户首次购买包年包月,即可享受全系列 85折优惠! 了解活动详情:
作者:艾阳坤
#技术探索 #云原生

2022年9月23日

Apache RocketMQ 在阿里云大规模商业化实践之路
阿里云消息队列 RocketMQ 商业化历程 RocketMQ 诞生于 2012 年,诞生即开源。2012~2015 年,RocketMQ 一直在通过内部电商业务打磨自身服务能力,并在 2015 年于阿里云上线公测。2016 年,阿里云 RocketMQ 完成商业化,同时被捐赠给 Apache 基金会,同年获得了年度受欢迎中国开源软件荣誉。 在 Apache 孵化期间,Apache RocketMQ 经历了快速发展,2017 年即毕业成为了 Apache 顶级项目。同年,Apache RocketMQ TLP RocketMQ 4.0 正式发布。此后,RocketMQ 4.0 经历了长足发展,期间阿里云商业和开源相辅相成、齐头并进,直到今天,共同迈入 RocketMQ 5.0 时代。 RocketMQ 5.0 发布后,阿里云商业会持续采取 OpenCore 的发展模式,秉承上游优先的社区发展原则,与社区一起将 RocketMQ 打造为一个超融合的数据处理平台。 阿里云消息队列产品矩阵 阿里云基于 RocketMQ 消息底座,构建了多元化的消息产品系列。 RocketMQ 是阿里云主打的消息品牌,互联网新兴业务领域首选的数据通道。消息队列 Kafka 是大数据的首选数据通道,微消息队列 MQTT 是移动互联网和物联网的数据通道,消息队列 RocketMQ 是传统业务领域的数据通道。消息服务 MNS 是 RocketMQ 轻量版,主要应用于应用集成领域,为平台型应用提供简单的队列服务。事件总线 Event Bridge 定位为云上事件枢纽,旨在阿里云上构建统一的事件中心。 阿里云消息队列产品矩阵完全构建在 RocketMQ 之上,基本实现了应用场景全覆盖,包括微服务解耦、SaaS 集成、物联网、大数据或日志收集生态,同时也在内部覆盖了阿里巴巴所有业务,在云上为数万阿里云企业提供了优质的消息服务。阿里云的消息产品矩阵涵盖了互联网、大数据、移动互联网等领域业务场景,为云原生客户提供不可或缺的一站式解决方案。 RocketMQ 在阿里云商业化历程中,一直致力于探索业务消息实践,也孵化了大量业务消息特性,并持续反哺到开源社区。 RocketMQ 4.0 业务消息探索之路 RocketMQ 在商业化过程中,陆续推出了四种消息类型来满足丰富的业务场景。 普通消息:普通消息提供极致弹性、海量堆积能力,内置重试与死信队列来满足业务对失败重试的需求,同时具备高吞吐、高可用、低延迟等特性,广泛应用于应用集成、异步解耦、削峰填谷等场景。 定时消息:提供秒级定时精度, 40 天超长定时,主要面向分布式定时调度、任务超时处理等场景,目前正在开源中。  顺序消息:支持全局与局部严格有序,从发送、存储到消费,保证端到端有序。面向有序事件处理、撮合交易、数据实时增量同步等场景。 事务消息:分布式、高性能、高可用的最终一致性事务解决方案,广泛应用于电商交易系统中服务的一致性协调场景并且已经开源。  RocketMQ 4.0 期间,商业和开源都致力于全方位拓展消息接入能力,使 RocketMQ 能够非常轻松地连接应用开源和云产品生态。比如商业上提供了多语言 SDK ,开源也有相应的 SDK 能够覆盖 Java、Go、Python 、C++使用 RocketMQ。同时支持 Spring 生态,能够通过 Spring Cloud 的方式使用 RocketMQ。商业上提供了一组非常简单易用的 HTTP API,提供了 67 种语言的实现。 除了 SDK 接入,RocketMQ 也在积极拥抱社区标准,在云产品侧提供了 AMQP 和 MQTT 的接入能力,其中 MQTT 已开源。 RocketMQ 也大力在发展 connector 生态,能够通过 RocketMQ connector 接入很多数据源,包括 Redis、MongoDB、Hudi 等大数据系统。 另外,阿里云构建的事件总线 EventBridge 也已开源,通过该产品能够将阿里云的云产品、SaaS 应用、自建数据平台的数据引入 RocketMQ。 RocketMQ 4.0 版本做了大量尝试,提供了全方位的消息接入能力。 RocketMQ 在服务阿里集团用户和商业化历程中,沉淀了大量领先的业务消息处理与服务能力。比如消息订阅方面,RocketMQ 支持集群分布式消费能力,也支持广播消费。在消息处理方面支持基于 Tag 和 SQL 做灵活过滤,其中基于 SQL 过滤是电商交易中非常重要的特性,能够支持在非常订阅比的情况下实现较低的投递比。 全球消息路由能力具备性能高、实时性强的特点。在云时代,数据中心天然分布在各个地域,各个地域之间还有 VPC 网络隔离。但是通过全球消息路由功能可以将地域与网络打通,能够满足更多业务场景。比如在阿里内部基于该能力实现了异地多活、异地容灾等企业级特性。 另外,全球消息路由具备非常高的易用性,提供了可视化任务管理界面,通过简单配置即可创建复制链路。 消息治理方面,RocketMQ 提供了访问控制、命名空间、实例限流、消息回放、重试消息、死信消息、堆积治理等能力。 服务能力方面,RocketMQ 经历了非常多沉淀,它在为交易链路服务了 12  年,参加了 10 年双 11,这也保证了 RocketMQ 能够在阿里云上提供非常高的可靠性。双 11 消息收发 TPS 峰值过亿,日消息收发总量超过 3 万亿。而即使在双十一万亿级数据洪峰下,消息也能做到 99.996% 毫秒级响应能力,消息发布平均响应时间不超过 3 毫秒,最大不超过 20 毫秒,真正实现了低延迟消息发布。 商业化初期,客户遇到最大难题是在分布式环境下如何完整地追踪异步消息链路。基于此背景,我们打造了可视化全生命周期消息轨迹追踪系统,能够提供丰富的消息查询、消息下载、定点重投、轨迹追踪能力,通过可观测系统帮助用户解决分布式环境中不可观测的问题。 如上图所示,一条消息从产生、发送至服务端存储到最终投递到消费者,整个发送和消费轨迹都有迹可循,包括投递给哪些消费者、哪些消费者在什么地方成功消费或者消费失败、何时进行重投,真正帮助客户解决了分布式观测难题。 除了功能特性,RocketMQ 在稳定性方面也做了很多建设。我们始终坚持,SLA 是云原生的根本,因此整个研发运维链路都有严格的稳定性保障措施: 架构开发:每个方案设计都会面向失败设计,代码开发阶段会有严格 Code Review 阶段,也会完整经历单元测试、集成测试、性能测试和容灾测试流程。 变更管理:有着非常严格的变更制度,要做到每个变更可灰度、可监控、可回滚、可降级。 稳定性防护:提供了限流、降级、容量评估、应急方案、大促保障等能力,会定期进行故障和预案演练,定期进行风险梳理。 体系化巡检:在云上有全方位的生产环境黑盒巡检。基于用户视角,会对全地域所有功能做全功能扫描,包含高达 50 多项检测项,任意项功能出问题都能立刻被监测到。在白盒巡检方面,会对 JVM 运行时指标、内核系统、集群指标进行巡检。 故障应急:有完整地故障应急流程,包括监控报警、故障发生、快速止血、排查根因、故障复盘。 RocketMQ 5.0 云原生架构升级之路 云原生时代,云上用户对云产品服务化程度、弹性能力、可控制性能力以及韧性都有了更高的要求。在此背景之下,我们对 RocketMQ 进行了云原生架构升级,这也是 RocketMQ 5.0 的诞生背景。 轻量级 SDK:基于云原生通信标准 gRPC 开发了一组轻量级 SDK,能够与当前富客户端优势互补。   无状态消息网关:在核心数据链路推出了无状态消息网关。通过搭建无状态服务节点Proxy,再通过 LB 进行服务暴露,将存储节点数据分离来独立负责核心消息存储和高可用。Proxy 与 Store 节点分离部署,独立弹性。  Leaderless 高可用架构:Store 节点身份完全对等,完全 Leaderless 化,去 ZK 和 HA 管控节点,能够做到非常高的可用性。同时相比传统的 Raft 一致性协议,该 Leaderless 架构能够做到副本数灵活选择,同步异步自动升降级,实现秒级故障转移。高可用架构目前已经完成开源并与 Dledger 进行了融合。  云原生基础设施:可观测验能力云原生化,OpenTelemetry 标准化。整体架构走向 Kubernetes 化,能够充分利用售卖区的资源弹性能力。 RocketMQ 4.0 推荐的接入方式主要是富客户端。富客户端提供了诸如客户端侧负载均衡、消息缓存、故障转移等一系列企业级特性。但在云原生时代,轻量级、高性能的客户端更容易被云原生技术栈所集成。 因此,RocketMQ 5.0 重磅推出了全新多语言轻量级 SDK,具有以下优势: 全新极简 API 设计:不可变 API,有完善的错误处理。多语言 SDK 保障 API 在 Native 层面对齐。同时引入了全新的 Simple Consumer,能够支持按消息模型进行消费,用户不再需要关心消息队列,只需要关注消息。  通信层采用 gRPC 协议:拥抱云原生通信标准,gRPC 能够使服务更易被集成。多语言 SDK 通信代码也可以通过 gRPC 快速生成,更 Native 。  轻量级实现:采用无状态消费模式,能够大幅降低客户端的实现复杂度。客户端更轻量,采用的应用也更容易被 Serverless化、Mesh 化。  云原生可观测性:客户端实现了 OpenTelemetry 标准,能够支持以 OpenTelemetry 形式导出 Metrics 与 Tracing。 RocketMQ 5.0 的另一个重大升级是引入了全新的无状态消费模型。该消费模型完全构建在原先的队列模型之上。队列模型是与存储模型一致的消费模型,消费者完全按照队列做负载均衡,也按照队列做消息拉取,非常适合批量高速拉取以及对单条消息状态不敏感的场景,比如流计算等。 RocketMQ 5.0 推出了 PoP 机制,巧妙地在队列模型之上构建了消息模型,实现了鱼与熊掌兼得。在此消息模型的设计上,业务可以只关心消息而无需关心队列,所有 API 都能够支持单条消息级别的消费、重试、修改不可见时间、删除。 在消息模型下,消息发送过来被存储后,即对消费者可见。消费者通过 Receive Message API 对消息进行消费后,消息进入定时不可见状态。消息超时过后又会重新处于可见状态,能被其他消费者继续消费。某消费者确认消息后,服务端会对该消息进行删除,随即不可见。 基于消息系模型的消费流程下,API 完全面向消息而不是面向队列。而当 PoP 机制遇见了无状态 Proxy,除了存储层,其他节点都是无状态的;客户端、连接和消费也是无状态的,可任意在 Proxy 节点上飘移,真正做到轻量级。 经过重构,RocketMQ 5.0 的可观测性也走向了云原生标准。 Metrics 侧: 指标涵盖丰富:设计了更丰富的指标,包含消息量、堆积量、各个阶段耗时等指标,每个指标从实例、Topic、消费 GroupID 多维度做聚合和展示。 消息团队实践模板:为用户提供实践模板,并持续迭代更新。 Prometheus + Grafana:Prometheus 标准数据格式,利用 Grafana 展示。除了模板,用户也可以自定义展示大盘。 Tracing 侧: OpenTelemetry Tracing 标准:RocketMQ Tracing 标准已经合并到 OpenTelemetry 开源标准,提供了规范和丰富的 messaging tracing 场景定义。 消息领域定制化展示:按照消息维度重新组织抽象的请求 span数据,展示一对多的消费,多次消费信息直观且方便理解。 可衔接 tracing 链路上下游:消息的 tracing 可继承调用上下文,补充到完整的调用链路中,消息链路信息串联了异步链路的上游和下游链路信息。 Logging 侧: Error Code 标准化:不同的错误有唯一的 Error Code。 Error Message 完整:包含完整的错误信息和排序所需要的资源信息。 Error Level 标准化:细化了各种不同错误信息的日志级别,用户可根据 Error、Warn 等级别配置更适合的监控告警。 弹性方面,RocketMQ 5.0 商业版能够充分撬动云的计算、存储和网络的池化资源。比如在计算方面,RocketMQ 5.0 所有工作负载完全部署在 ACK 之上,充分利用了 ACK 弹性能力,撬动 ACK 弹性资源。主要依赖 ACK 的两项技术,一是弹性资源池,另一个是 HPA 支持计算能力快速弹性。同时也会在 ACK 之上做跨可用区部署以提供高可用保障。 网络层面,RocketMQ 5.0 也会充分利用阿里云网络设施,为用户提供更便捷的网络访问能力。比如 RocketMQ 5.0 实例能够支持公网随开随用,需要依赖公网做测试的时候即开即用,测试完立即关闭,安全与方便兼具。同时支持多种私网类型的网络形态,包括 Single Tunnel、Private Link,另外也基于 CEN 构建了全球互通设计网络。 存储方面,RocketMQ 5.0 商业版率先引入多级存储概念,基于 OSS 构建二级存储,能够充分利用 OSS 存储的弹性能力,存储计费也转向了按量付费。而用户能够在 RocketMQ 之上自定义消息存储时长,比如将消息从 3 天有效时长延长至 30 天,能够真正将消息变为数据资产。同时利用二级存储能力,将冷热数据分离,为用户提供一致的冷读 SLA 。 RocketMQ 5.0 商业版发布预告 RocketMQ 4.0 历经了五年发展,开源和商业版本共同迈入了 5.0 时代。7 月底,阿里云消息队列将会基于开源版发布全新的 5.0 商业化版本。注:截止发稿前,RocketMQ 5.0 已经在阿里云消息队列 RocketMQ 产品上全新发布,目前支持国内主要地域。 RocketMQ 5.0 版相对于 4.0 版实例主要有以下几大改变: 第一,新版本、新售卖,更便宜。新版本采取了全新计量方式,有包年、包月型,也有按量付费和公网流量弹性计费。也有更全的售卖体系,比如新增专业版实例,能够满足部分用户需求。同时每个商品系列都新增了测试环境专用实例,能够方便用户以低成本的方式搭建自己的开发环境。 第二,更强弹性,降本提效利器。存储完全走向弹性,能够通过 Serverless 按需使用,按量付费。预留弹性,实例基础规格支持实时升降配,用户可以很方便地在流量到来之前做弹性。此外,专业版支持突发流量弹性,能够解决线上稳定性风险。 第三,全新架构,增强可观测运维。无状态消息消费模型能够解决一些老版本的痛点。同时在可观测上全面采取了云原生接入栈。 消息的全新形态:事件总线 EventBridge 事件总线 EventBridge 已经开源到 RocketMQ 社区中。云原生时代,事件无处不在,云计算资源散落在各地,各类生态孤岛随处可见。因此,以事件和事件驱动的方式来集成这一切是大势所趋。 基于此,阿里云推出了全新事件型产品 EventBridge。该产品构建在 RocketMQ 之上,是 RocketMQ 之上的一个事件驱动架构实践。 EventBridge 的事件源包括阿里云服务的管控事件比如资源变更事件、审计事件、配置变更事件,阿里云服务的数据事件,也包括自定义应用、SaaS 应用、自建数据平台、其他云厂商服务等。 事件经过 EventBridge 处理后会投递到事件目标,事件目标包括函数计算、消息服务、自建网关、HTTP(S)、短信、邮箱、钉钉等。 事件源到事件目标之间会经历完整的事件处理,包括事件源接入到 EB 后,可以对事件进行过滤、转换、归档、回放等。事件在 EventBridge 整个流程中也有完善的可观测性设计,包括事件查询、链路追踪。事件的接入方式非常丰富,可以通过 OpenAPI 来接入、7 种多语言 SDK、CloudEvents SDK、Web Console 和 Webhook 。 EventBridge 具有如下特点: 能够大幅度减少用户开发成本,用户无需额外开发,通过创建 EventBridge 源、事件目标、事件规则等资源即可实现事件架构。用户可以编写事件规则,对事件做过滤、转换。  提供原生 CloudEvents 支持,拥抱 CNCF 社区,能够无缝对接社区 SDK 。标准协议也能统一个阿里云事件规范。  事件 Schema 支持:能够支持事件 Schema 自动探测和校验,支持 Source 和 Target 的 Schema 绑定。  全球事件任意互通:组建了全球事件任意互通网络,组件了跨地域、跨账户的事件网络,能够支持跨云、跨数据中心的事件路由。 EventBridge在云上生态已经初具规模,已经集成了 255+ 云产品事件源和 1000+ 事件类型。 EventBridge率先对消息生态做了融合。阿里云的消息产品矩阵生态均通过 EventBridge 做了完全融合。任何一款消息产品与另一款消息产品的数据都能互通。同时,依靠 EventBridge 的全球事件网络,能够为所有消息产品赋予全球消息路由的能力。 EventBridge 目前已经在内部接入钉钉 ISV、聚石塔 ISV,外部也有 50+ SaaS 系统可以通过 Webhook 的方式接入。另外,海量事件源可以触达 10 多种事件目标,已经对接了全系云产品 API ,任何事件都可以驱动全量云产品 API。 作者介绍: 周新宇 Apache Member,Apache RocketMQ PMC Member,阿里云消息队列 RocketMQ 研发负责人。 活动推荐 阿里云基于 Apache RocketMQ 构建的企业级产品消息队列RocketMQ 5.0版现开启活动: 1、新用户首次购买包年包月,即可享受全系列 85折优惠! 了解活动详情:
作者:周新宇
#行业实践 #云原生

2022年8月29日

阿里云基于全新 RocketMQ 5.0 内核的落地实践
前言 在上个月结束的 RocketMQ Summit 全球开发者峰会中,Apache RocketMQ 社区发布了新一代 RocketMQ 的能力全景图,为众多开发者阐述 RocketMQ 5.0 这一大版本的技术定位与发展方向。 在过去七年大规模云计算实践中,RocketMQ 不断自我演进,今天,RocketMQ 正式迈进 5.0 时代。 从社区关于 5.0 版本的解读可以看到,在云原生以及企业全面上云的大潮下,为了更好地匹配业务开发者的诉求,Apache RocketMQ 做了很多的架构升级和产品化能力的适配。那么如何在企业的生产实践中落地 RocketMQ 5.0 呢?本篇文章的核心就消息架构以及产品能力的云原生化,介绍了阿里云是如何基于全新的 RocketMQ 5.0 内核做出自己的判断和演进,以及如何适配越来越多的企业客户在技术和能力方面的诉求。 云原生消息服务的演进方向 首先我们来看下云原生消息服务有哪些演进? 面向未来,适应云原生架构的消息产品能力应该在以下方面做出重要突破: 大规模弹性:企业上云的本质是解放资源供给的负担和压力,专注于业务的集成和发展。作为消息服务的运维方,应该为上层业务提供与模型匹配的资源供给能力,伴随业务流量的发展提供最贴合的弹性能力。一方面可以解决面向不确定突发流量的系统风险,另一方面也可以实现资源利用率的提升。  易用性:易用性是集成类中间件的重要能力,消息服务应该从 API 设计到集成开发、再到配置运维,全面地降低用户的负担,避免犯错。低门槛才能打开市场,扩大心智和群体。   可观测性:可观测性对于消息服务的所有参与方来说都很重要,服务提供方应提供边界清晰、标准开放的观测诊断能力,这样才能解放消息运维方的负担,实现使用者自排查和边界责任的清晰化。  稳定性高 SLA:稳定性是生产系统必备的核心能力,消息来说往往集成在核心交易链路,消息系统应该明确服务的可用性、可靠性指标。使用方应基于明确的 SLA 去设计自己的故障兜底和冗余安全机制。 立足于这个四个关键的演进方向,下面为大家整体介绍一下阿里云 RocketMQ 5.0 在这些方面是如何落地实践的。 大规模弹性:提供匹配业务模型的最佳资源供给能力 消息服务一般集成在业务的核心链路,比如交易、支付等场景,这一类场景往往存在波动的业务流量,例如大促、秒杀、早高峰等。 面对波动的业务场景,阿里云 RocketMQ 5.0 的消息服务可以伴随业务的诉求进行自适应实现资源扩缩。一方面在比较稳定的业务处理基线范围内,按照最低的成本预留固定的资源;另一方面在偶尔存在的突发流量毛刺时,支持自适应弹性,按量使用,按需付费。两种模式相互结合,可以实现稳定安全的高水位运行,无需一直为不确定的流量峰值预留大量资源。 除了消息处理流量的弹性适应外,消息系统也是有状态的系统,存储了大量高价值的业务数据。当系统调用压力变化时,存储本身也需要具备弹性能力,一方面需要保障数据不丢失,另一方面还需要节省存储的成本,避免浪费。传统的基于本地磁盘的架构天然存在扩缩容问题,其一本地磁盘容量有限,当需要扩大容量时只能加节点,带来计算资源的浪费;其二本地磁盘无法动态缩容,只能基于业务侧流量的隔离下线才能缩减存储成本,操作非常复杂。 阿里云 RocketMQ 5.0 的消息存储具备天然的 Serverless 能力,存储空间按需使用,按量付费,业务人员只需要按照需求设置合理的 TTL 时间,即可保障长时间存储时的数据完整性。 集成易用性:简化业务开发,降低心智负担和理解成本 集成易用性是一种系统设计约束,要求消息服务应该从 API 设计到集成开发、再到配置运维,全面地降低用户的负担,避免犯错。举个典型场景,在消息队列例如 RocketMQ 4.x 版本或 Kafka 中,业务消费消息时往往被负载均衡策略所困扰,业务方需要关注当前消息主题的队列数(分区数)以及当前消费者的数量。因为消费者是按照队列粒度做负载均衡和任务分配,只要消费者能力不对等,或者数量不能平均分配,必然造成部分消费者堆积、无法恢复的问题。 在典型的业务集成场景,客户端其实只需要以无状态的消息模型进行消费,业务只需关心消息本身是否处理即可,而不应该关心内部的存储模型和策略。 阿里云 RocketMQ 5.0 正是基于这种思想提供了全新的 SimpleConsumer 模型,支持任意单条消息粒度的消费、重试和提交等原子能力。 可观测性:提供边界清晰、标准开放的自助诊断能力 有运维消息队列经验的同学都会发现,消息系统耦合了业务的上游生产和下游消费处理,往往业务侧出问题时无法清晰地界定是消息服务异常还是业务处理逻辑的异常。 阿里云 RocketMQ 5.0 的可观测性就是为这种模糊不确定的边界提供解法,以事件、轨迹、指标这三个方面为基础,依次从点、线、面的纬度覆盖链路中的所有细节。关于事件、轨迹、指标的定义涵盖如下内容: 事件:覆盖服务端的运维事件,例如宕机、重启、变更配置;客户端侧的变更事件,例如触发订阅、取消订阅、上线、下线等;   轨迹:覆盖消息或者调用链的生命周期,展示一条消息从生产到存储,最后到消费完成的整个过程,按时间轴抓出整个链路的所有参与方,锁定问题的范围;  指标:指标则是更大范围的观测和预警,量化消息系统的各种能力,例如收发 TPS、吞吐、流量、存储空间、失败率和成功率等。 阿里云 RocketMQ 在可观测性方面也是积累良多,不仅率先支持了完善的消息轨迹链路查询,而且在 5.0 新版本中还支持将客户端和服务端的 Trace、Metrics 信息以标准的 OpenTelemetry协议上报到第三方Trace、Metrics中存储,借助开源的 Prometheus 和 Grafana 等产品可以实现标准化的展示和分析。 稳定性 SLA:提供可评估、可量化、边界明确的服务保障能力 稳定性是生产系统必备的核心能力,消息系统往往集成在核心交易链路,消息系统是否稳定直接影响了业务是否完整和可用。但稳定性的保障本身并不只是运维管理,而是要从系统架构的设计阶段开始梳理,量化服务边界和服务指标,只有明确了服务的可用性和可靠性指标,使用方才能设计自己的故障兜底和冗余安全机制。 传统的基于运维手段的被动保障方式,只能做基本的扩缩容和系统指标监控,对于消息的各种复杂边界场景,例如消息堆积、冷读、广播等并不能很好的提供量化服务能力。一旦上层业务方触发这些场景,系统则会被打穿,从而丧失服务能力。 阿里云 RocketMQ 5.0 体系化的稳定性建设,是从系统设计阶段就提供对消息堆积、冷读等场景量化服务的能力,确定合理的消息发送 RT、端到端延迟和收发吞吐 TPS 能力等,一旦系统触发这些情况,可在承受范围内做限制和保护。 本篇文章从大规模弹性、集成易用性、可观测性和稳定性 SLA 等方面介绍了 RocketMQ 5.0 的演进和方向,同时针对性介绍了阿里云消息队列 RocketMQ 5.0 在这些方面的实践和落地。 阿里云消息队列 RocketMQ 5.0 目前已正式商业化,在功能、弹性、易用性和运维便捷性等方面进行了全面增强,同时定价相比上一代实例最高降低 50%,助力企业降本增效,以更低的门槛实现业务开发和集成。新一代实例支持 0~100 万 TPS 规模自伸缩、支持突发流量弹性和存储 Serverless;在可观测性方面,支持全链路轨迹集成和自定义 Metrics 集成;在集成易用性方面,支持新一代轻量原生多语言 SDK,更加稳定和易用。 活动推荐 阿里云基于 Apache RocketMQ 构建的企业级产品消息队列RocketMQ 5.0版现开启活动: 1、新用户首次购买包年包月,即可享受全系列 85折优惠! 了解活动详情:
#行业实践 #云原生