作者|绍舒
审核&校对:岁月、佳佳
编辑&排版:雯燕
消息队列是分布式互联网架构的重要基础设施,在以下场景都有着重要的应用:
并涉及互动直播、移动互联网&物联网,IM 实时通信、Cache 同步、日志监控等多个领域。
而本文主要围绕着商业版本的消息队列 RocketMQ,和开源版本 RocketMQ 进行比较,并结合一些实践中的场景来展示大型分布式应用的上云最佳实践。
商业版本消息队列 RocketMQ 相比较开源版本 RocketMQ 和其他竞品,主要有以下几点优势。
消息队列 RocketMQ 提供了定时、事务、顺序等多类型消息的支持,且支持广播、集群两种消费模式;另外在协议层面,提供 TCP/HTTP 多协议支持,还提供了 TAG/SQL 属性过滤功能,极大程度地拓宽了用户的使用场景。
消息队列 RocketMQ 经受了阿里核心电商历年双十一洪峰的考验,支持千万级 TPS 消息收发和亿级消息堆积的能力,并且能够为消息提供毫秒级端到端延迟保障,另外还提供分级存储,支持海量消息的任意保存时间。
消息队列 RocketMQ 提供了一个可观测性大盘,支持细粒度数据大盘,提供了消息全链路生命周期追踪和查询能力,对各个指标提供了相应的监控报警功能;此外,还提供了消息回溯和死信队列功能,能够保证用户的消息能够随时回溯消费。
消息队列 RocketMQ 的稳定性是我们一贯、持续、稳定投入的重要领域,提供了高可用部署和多副本写入功能;另外也支持同城多 AZ 容灾和异地多活。
接下来,我们会从以上的产品核心能力中挑选几个剖面,并且结合具体的场景和实践来做进一步的介绍。
商业版本消息队列 RocketMQ 使用的顺序消息我们称之为高可用顺序消息。在介绍高可用顺序消息之前,首先简要介绍下开源版本 RocketMQ 的顺序消息。
顺序消息分为两种类型,全局顺序消息和分区顺序消息。
下图是分区顺序消息的应用场景,order ID 即为此时顺序消息的 Sharding Key。
可以看到,无论是全局顺序消息还是分区顺序消息,都依赖了单一分区天然的 FIFO 特性来保证顺序,因此顺序性也只能在同一个分区内保证,当此分区所在的副本不可用时,顺序消息并不具备重试到其他副本的能力,此时消息的顺序性就难以得到保证。
为了解决这一问题,我们设计并实现了高可用顺序消息。
高可用顺序消息有以下几个特点:
通过这样的设计,高可用顺序消息解决了下列几点问题:
尤其需要注意的是热点问题,在阿里巴巴内部某电商业务大促时,因发送到顺序 Topic 的某一特定的 ShardingKey 数量过多,集群中一个副本接收到了大量该 ShardingKey 的消息,导致该副本超出其负荷上限,造成了消息的延迟和堆积,一定程度上影响了业务。在使用了高可用顺序消息之后,由于其在多物理分区中的负载均衡特性,提升了集群顺序消息的承载能力,从而避免了热点问题的出现。
定时消息,是指客户端当前发送但希望在未来的某个时间内收到的消息。定时消息广泛应用于各类调度系统或者业务系统之中。比如支付订单,产生一个支付消息,系统通常需要在一定时间后处理该消息,判断用户是否支付成功,然后系统做相应处理。
开源版本的 RocketMQ 只支持几个指定的延迟级别,并不支持秒级精度的定时消息。而面向集团内和云上多样化的需求,开源版本的定时消息并不能满足我们的需求,因此我们推出了秒级精准定时消息。
如下图所示,我们基于时间轮设计并实现了支持任意定时时间的秒级精准定时消息,同时满足以下特性:
内部某用户有这样的场景,期望在未来的某一分钟的 30s 时刻处理这样一个定时请求,开源版本的定时消息并不符合其需要,而秒级精准定时消息在保证高可用、高性能的同时,满足了其业务需求。
如下图所示,在传统的事务处理中,多个系统之间的交互耦合到一个事务中,造成整体的相应时间长,回滚过程复杂,从而潜在影响了系统的可用性;而 RocketMQ 提供的分布式事务功能,在保证了系统松耦合和数据最终一致性的前提下,实现了分布式事务。
消息队列 RocketMQ 提供的事务消息处理步骤如下:
基于这样的实现,我们通过消息实现了分布式事务特性,即本地事务的执行结果会最终反应到订阅方是否能接收到该条消息。
消息队列 RocketMQ 的分布式事务消息广泛地应用于阿里巴巴核心交易链路中,通过分布式事务消息,实现了最小事务单元;交易系统和消息队列之间,组成一个事务处理;下游系统(购物车、积分、其它)相互隔离,并行处理。
随着云上客户的不断增多,存储逐渐成为 RocketMQ 运维的重要瓶颈,这包括并且不限于:
基于以上现状,分级存储方案应运而生。
分级存储的整体架构如下:
通过这样的设计,我们实现了消息数据的冷热分离。
基于分级存储,我们进一步拓展了用户的使用场景:
消息队列 RocketMQ 的稳定性是我们一贯、持续、稳定投入的重要领域。在介绍我们在稳定性的最新工作之前,首先带大家回顾下 RocketMQ 高可用架构的演进路线。
2012 年,RocketMQ 作为阿里巴巴全新一代的消息引擎问世,并随后开源至社区,第一代 RocketMQ 高可用架构也随之诞生。如下图所示,第一代高可用架构采取当时流行的 Master-Slave 主从架构,写流量经过 Master 节点同步至 Slave 节点,读流量也经过 Master 节点并将消费记录同步至 Slave 节点。当 Master 节点不可用时,整个副本组可读不可写。
2016 年,RocketMQ 云产品正式开始商业化,云时代单点故障频发,云产品需要完全面向失败而设计,因此 RocketMQ 推出了第二代多副本架构,依托于 Zookeeper 的分布式锁和通知机制,引入 Controller 组件负责 Broker 状态的监控以及主备状态机转换,在主不可用时,备自动切换为主。第二代架构是消息云产品规模化进程中的核心高可用架构,为云产品规模化立下了汗马功劳。
2018 年,RocketMQ 社区对 Paxos 和 Raft 引入分布式协议有极大的热情,RocketMQ 研发团队在开源社区推出了基于 Raft 协议的 Dledger 存储引擎,原生支持 Raft 多副本。
RocketMQ 高可用架构已经走过了三代,在集团、公有云和专有云多样场景的实践中,我们发现这三套高可用架构都存在一些弊端:
为了应对云上日益增长的业务规模、更严苛的 SLA 要求、复杂多变的专有云部署环境,当前的消息系统需要一种架构简单、运维简单、有基于当前架构落地路径的方案,我们将其称作秒级 RTO 多副本架构。
秒级 RTO 多副本架构是消息中间件团队设计实现的新一代高可用架构,包含副本组成机制、Failover 机制、对现有组件的侵入性修改等。
整个副本组有以下特点:
在上述副本组的概念下,故障转移可以复用当前 RocketMQ 客户端的机制来完成。如下图所示:
我们在可观测性方面也做了大量的工作,为用户提供了一个消息系统的可观测性健康数据大盘。如下图所示,用户能够清晰的看到实例级别、topic 级别、group 级别的各种监控数据,能够全方面地监控、诊断问题。
另外我们还基于消息轨迹提供了消息全链路轨迹追踪功能。如下图所示,用户能够在控制台上看到完整的消息生命周期、从消息的发送、存储、到消费,整个链路都能被完整地记录下来。
客户痛点:业务出现消费堆积的用户需要根据消息轨迹抽样数据,综合分析后才能大致判断引起问题原因,排查困难。
核心价值:提高线上运行问题排查的效率,和问题定位的准确性。直接在健康大盘上快速发现风险最高的 Topic 和 Group,并根据各个指标的变化情况快速定位原因。例如消息处理时间过长可以扩容消费者机器或优化消费业务逻辑,如果是失败率过高可以快速查看日志排除错误原因。
大家一定非常熟悉 Gartner,在2018年的一个评估报告里,Gartner 将 Event-Driven Model,列为了未来10大战略技术趋势之一,并且,做出了两个预测:
同一年,CNCF 基金会也提出了 CloudEvents,意在规范不同云服务之间的事件通讯协议标准。到目前为止,CloudEvents也已经发布了多个消息中间件的绑定规范。
可见事件驱动是未来业务系统的一个重要趋势,而消息天然具备和事件的亲近性,因此消息队列 RocketMQ,是坚决拥抱事件驱动的。
谈到消息和事件,这里做一个简单的阐述:消息和事件是两种不同形态的抽象,也意味着满足不同的场景:
在2020年,阿里云发布了事件总线 EventBridge 这一产品,其使命是作为云事件的枢纽,以标准化的 CloudEvents 1.0 协议连接云产品和云应用,提供中心化的事件治理和驱动能力,帮助用户轻松构建松耦合、分布式的事件驱动架构;另外,在阿里云之外的云市场上有海量垂直领域的 SaaS 服务,EventBridge 将以出色的跨产品、跨组织以及跨云的集成与被集成能力,助力客户打造一个完整的、事件驱动的、高效可控的上云新界面。
而借助事件总线 EventBridge 提供的事件源功能,我们能够打通消息到事件的链路,使得消息队列 RocketMQ 具备事件驱动的动力,从而拥抱整个事件生态。接下来我们将借助一个案例,如下图所示,为大家展示这一功能。
我们基于容器服务快速创建一个事件驱动的服务,计算负载 Deployment 的 yaml 如下,该服务能够响应事件并将结果打印到标准输出中。
apiVersion: apps/v1 # for versions before 1.8.0 use apps/v1beta1 kind: Deployment metadata: name: eventbridge-http-target-deployment labels: app: eventbridge-http-target spec: replicas: 2 selector: matchLabels: app: eventbridge-http-target template: metadata: labels: app: eventbridge-http-target spec: containers: - name: eb-http-target # 下述镜像暴露了一个 HTTP 地址(/cloudevents)用于接收 CloudEvents,源码参考:https://github.com/aliyuneventbridge/simple-http-target image: registry.cn-hangzhou.aliyuncs.com/eventbridge-public/simple-http-target:latest ports: - containerPort: 8080
前往容器服务控制台,进入服务与路由的服务页面,创建一个私网访问类型的 Service,并做好端口映射。
我们来到事件总线 EventBridge 控制台,创建一个自定义总线 demo-with-k8s。
我们为总线 demo-with-k8s 创建一个规则,并选择 HTTP 作为事件目标,选择专有网络类型,选中对应的 VPC、 VSwitch 以及安全组,并指定目标URL,如下图所示:
我们为该自定义事件总线添加消息队列 RocketMQ 版的自定义事件源。
接下来我们回到消息队列 RocketMQ 控制台,通过控制台的快速体验消息生产功能发送一条内容为 hello eventbridge 的消息到对应的主题中去。
接下来我们就可以发现,这条 RocketMQ 消息,以 CloudEvent 的形式被投递到了对应的服务中去,我们从而打通了消息到事件的链路。同时,基于我们上述提到的分级存储功能,消息队列 RocketMQ 转变成了一个能够源源不断提供事件的数据仓库,为整个事件生态提供了更加广阔的场景。
事件驱动是未来商业组织和业务系统的重要趋势,而消息队列 RocketMQ 会坚定地拥抱这一趋势,将消息融入到事件的生态中。
我们选取了消息队列 RocketMQ 的几个产品剖面,从多消息类型、分级存储到稳定性、可观测性,再到面向未来的事件驱动,并结合与开源 RocketMQ 的对比,及具体应用场景的分析,为大家展示了基于消息队列 RocketMQ 的大型分布式应用上云最佳实践。