作者:曾林(九析),政采云基础平台负责人
本文摘要:
1、利用RocketMQ进行灰度和流量染色
2、利用Dledger进行自动容灭切换提高稳定性
3、基于RocketMQ延迟消息做个性化定制
4、基于RocketMQ做云原生运行时改造落地
RocketMQ架构
1、RocketMQ架构
关于RocketMQ的架构,我们通过一个小故事来通俗易懂的讲解下。
小P(producer)和小C(consumer)是大学里的一对情侣,他们分别住在192.168.0.69宿舍和192.168.0.96宿舍,小P经常会写情书给小C,并在两人约会的时候交给她。
毕业了,小P被杭州某互联网大厂录取,小C哭着说:“你要去做996的工作,如果哪天猝死了谁还给我写情书呢?”于是聪明的小P想到一个办法,他说:“我会写很多情书并把它们寄到192.168.0.88宿舍的 Broker 邮箱里,如果哪天我不在了,你可以去那里找。”
小P到杭州工作后十分思念小C,每天都写很多信寄到Broker 邮箱,慢慢的一个邮箱已经装不下了,小C向小P抱怨信太多了,于是小P把信寄到192.168.0.89或192.168.0.90宿舍邮箱,这样就解决了一个邮箱爆满的问题。
然而慢慢的新问题又出现了,3个信箱对于发信的小P和收信的小C来说都产生了麻烦,一边小P需要查哪个邮箱空闲可以发,一边小C需要查从哪个邮箱收信。于是小P又想了一个办法,他跟小C说:“咱们不用自己去查了,都统一问一下负责信件处理的隔壁老王,他会告诉我们要发到哪里再从哪里收。”问题解决了,小P和小C又可以幸福的写信和收信了。
将上图进一步扩展,加入多个小P、小C、邮箱和老王,就演变成 RocketMQ 架构。
RocketMQ架构图
2、RocketMQ部署模式
RocketMQ 的部署主要是针对 Broker 的部署。有以下四种基本部署模式:
a. 单机模式
如下图所示,单机模式部署单一的 Broker Master,如上文提到的小P和小C最初的通信方式。
单机模式的优点是简单,同步刷盘信息不会丢失。然而它的缺点也是显而易见的,如果宕机会导致服务不可用。因此单机模式并不可靠。
单机模式架构图
b. 多主模式
多主模式顾名思义就是部署多个 Broker Master 节点。
多主模式的优点是生产者发送的数据会分别存入不同的Broker 中,能够有效避免某个 Broker 直接收处理数据导致负载过高;单个 Master 宕机或重启维护对应无影响。它的缺点是单台服务器宕机期间,不可订阅该服务器上未被消费者消费的消息,只有机器恢复后才可恢复订阅,所以可能会影响消息的实时性。
多主模式架构图
c. 双主双从/多主多从模式(异步复制)
双主双从/多主多从模式是部署多个 Broker Master,同时也会为各个Broker Master部署一个Broker Slave,Master 和 Slave 之间采用“异步复制数据”进行数据同步。生产者将消息发送到 Broker Master 后不必等待数据同步到 Slave 节点,就返回成功。
这个模式的优点在于高性能,磁盘损坏也不会丢失大量消息,实时性不会受影响,Master宕机后,消费者仍然可以从Slave消费。然而由于主备有毫秒级短暂消息延迟,仍然会丢失有少量消息。
双主双从/多主多从模式(异步复制)架构图
d. 双主双从/多主多从模式(同步双写)
为了解决上面提到的问题,引入了同步双写功能,即部署多个 Broker Master,同时也会为各个 Broker Master 部署一个 Broker Slave,Master 和 Slave 之间采用“同步复制数据”进行数据同步。生产者将消息发送到 Broker Master后等待数据同步到 Slave 节点成功后,才返回成功。
这种模式的优点是数据与服务都无单点故障,Master宕机情况下,消息无延迟,具有较高的服务可用性与数据可用性。然而相比异步复制,它的性能会略低一些(大约低10%左右),发送单个消息的RT会略高,且目前版本在主节点宕机后,备机不能自动切换为主机。
双主双从/多主多从模式(同步双写)架构图
3、由RocketMQ进行知识点延伸
在分布式体系中,保持数据一致性的通用解决方案为:
• 异步复制(弱一致性)
• 同步双写(强一致性)
我们介绍了 RocketMQ 的四种部署模式,这四种模式是否可以完美解决一切问题?答案是否定的,无论使用哪种模式,当 Broker Master 宕机的时候都需要人为介入。如何实现故障转移,即如何自动选主呢?自动选主的意思是如果Master宕机,可以自动从 Slaver 中选出一个新的 Master。
以下是两种常用的自动选主解决方案:
a. 方案一、通过第三方完成选主,比如 Zookeeper
这个方案是借助 Zookeeper 在多个 Slaver 中自动选取一个新的 Master。
• 优点:借用第三方选主能力,无需自己实现;
• 缺点:这种方案会引入重量级外部组件,使部署变得复杂,同时也会增加运维成本,比如
在维护 RocketMQ 集群的同时还需要维护 Zookeeper 集群,如果 Zookeeper 集群出现故障也会影响 RocketMQ 集群;
b. 方案二、利用raft协议来完成自动选主
• 优点:不需要引入外部组件,节点之间通过通信就可以完成选主;
• 缺点:选主逻辑要自己实现,集成到各个节点的进程中。
RocketMQ DLedger
1、RocketMQ DLedger模式
为了更好的实现自动选主,我们将 RocketMQ 架构中 Broker 进行重新部署。如下图所示,Broker 集群由三组相同名称的 Broker 组成,至少需要3个节点,通过Raft选举机制自动选举出一个 Leader,其余节点作为 Follower,并在 Leader 和 Follower 之间复制数据以确保高可用。当Master 节点出现宕机后自动容灾切换,并保证数据一致性。这个模式需要使用 RocketMQ 4.5 及更高版本支持,同时它的性能会略低一些。
RocketMQ DLedger模式架构图
2、主从模式和DLedger模式对比
主从模式的本质是主仆关系,即使 Master 宕机,Slaver 也不能成为 Master;而 DLedger 模式的本质是民主选举制,Leader 是通过raft协议自动选举出来的,因此DLedger 模式基本解除了 RocketMQ 自动选主的痛点。
RocketMQ & Dapr
1、Dapr的官方定义
Dapr 是一个可移植的、事件驱动的运行时,它使任何开发人员能够轻松构建出弹性的、无状态和有状态的应用程序,并可运行在云平台或边缘计计算中,它同时也支持多种编程语和开发框架。
这个定义可能会让很多人觉得晦涩难懂,看完也不能理解Dapr 到底是什么,让我们换个角度来思考,从技术人员日常会遇到的痛点出发。
目前,基于微服务的分布式架构体系被广泛使用,微服务简而言之就是将成百上千的服务放在成千上万的节点上运行,这其中的痛点主要有以下几方面:
a. 运维之痛
体现在发布、健康检查、动态扩缩容等等;
解决方案:Docker(CRI)& Kubernetes;
b. 服务网络访问之痛
体现在重试、超时、断路器、流量管理、可观测性等等;
解决方案一:SDK方式
使用 SDK 方式需要编写相应的 annotation 代码执行;
解决方案二:服务网格(Service Mesh)
如下图所示,两台电脑上分别有两个服务Service A & Service B,在每个服务之间各加一个 Sidecar,通过 Sidecar 进行传输。
使用 Sidecar 方式无需编写代码,只要在 Sidecar 中加相应的规则即可。
对比以上两种解决方案,服务网格较 SDK 的优势在于:
• 将SDK从服务依赖中去除,将annotation从代码里删除;
• 通讯层全部交给“经纪人”(sidecar)处理,对业务完全透明;
• 在 SDK 方式下,业务服务对配置升级强感知,当交给平台层后,业务应用对配置升级无感知;
• 业务归业务,网络治理归 sidecar;业务与网络治理解耦。
c. 中间件管理之痛
• 不同中间件的使用方式和运维方式各不相同;
• 中间件SDK升级业务线和测试线强感知;
• 中间件SDK升级本是技术工作,到最后却发展成为架构团队全产研推动工作;
• 语言相关性,不同语言需要开发不同的客户端,同样逻辑不同实现会造成大量重复而无意义的工作。
解决方案一:SDK方式
以 RocketMQ 为例,下图是执行消息发送的代码:
是否可以在操作 RocketMQ 或其他中间件时不必引入 SDK ?能不能所有中间件使用都可以标准化?
解决方案二:Dapr
首先回顾一下传统 SDK 调用方式,如下图所示,在RocketMQ 中有一个 topic“wudi”,当 Producer 向 wudi 发送消息,Consumer 从 wudi中 接收/订阅 消息。
基于 Dapr 的使用方式如下图,Producer 和 Consumer 不再直接关联 RocketMQ,而是通过 Dapr 进行关联。
2、Dapr & RocketMQ 样例操作步骤
基于 Dapr 的 RocketMQ 操作需要9个步骤:
Step 1: 安装 Go SDK 并配置环境 <难度低>
Step 2: Dapr 源码编译 <难度中>
Step 3: 启动并运行 RocketMQ <难度低>
Step 4: 编写 pub/sub 为 RocketMQ 类型的 yaml<难度低>
Step 5: 编写订阅者 yaml<难度低>
Step 6: 编写订阅者代码逻辑(JAVA语言)<难度更低>
Step 7: 启动订阅者,对外服务端口为 8080 <难度更低>
Step 8: 启动 Daprd(自托管模式)<难度低>
Step 9: 向 RocketMQ 投递消息,订阅者接收并消费消息 <难度低>
Step 1: 安装Go SDK并配置环境
wget https://golang.google.cn/dl/go1.17.5.linux-amd64.tar.gz ( Go版本v1.17 以上) tar -zxvf go1.17.5.linux-amd64.tar.gz -C /usr/local mkdir /root/go && mkdir /root/go/src && mkdir /root/go/pkg && mkdir /root/go/bin vi ~/.bash_profile export GOROOT=/usr/local/go export PATH=$PATH:$GOROOT/bin export GOPATH=/root/go source ~/.bash_profile go version
Step 2: Dapr源码编辑/编译
cd /root/go/src git clone https://github.com/dapr/dapr.git -b release-1.5.1 cd dapr go env -w GOPROXY=https://goproxy.io,direct modify source code -> Next Page
这一步需要修改2个内容,首先打开文件
vim /root/go/src/dapr/cmd/daprd/main.go
第一处修改是添加 RocketMQ pkg,在 main.go 文件下找到74行复制粘贴73行并将 RabbitMQ 改成 RocketMQ;
第二处修改是初始化 Rocketmq loader,找到285行并加入下面红框中的代码;
修改好后,回到Dapr根目录cd dapr执行下面两步,
go mod tidy make build
以上步骤完成后,新的Dapr二进制文件就编译完成了。
Step 3: 启动并运行RocketMQ (略)
Step 4: 编写pub/sub为rocketmq类型的 yaml
步骤4和5是编写2个资源配置文件,一个文件是 Dapr 与RocketMQ 关联,一个文件是 Dapr 与 Consumer 关联。与 RocketMQ 关联的代码如下:
apiVersion: dapr.io/v1alpha1 kind: Component metadata: name: rocketmq-pubsub namespace: default spec: type: pubsub.rocketmq version: v1 metadata: - name: nameServer value: “xxx.xxx.xxx.xxx:9876” - name: accessProto value: "tcp"
其中需要特别关注以下几点:
• kind: 指定 dapr component 资源组件;
• type: 指定该资源组件为 pubsub 构建块的 RocketMQ 类型;
• metadata下的value指定为 tcp,指向自建 RocketMQ,如果是 http,指向阿里云 RocketMQ(ons);
• 该资源配置告诉 dapr 如何连接 RocketMQ 。
Step 5: 编写消费订阅者 yaml
这一步编写 Dapr 与 Consumer 关联文件,代码如下:
apiVersion: dapr.io/v1alpha1 kind: Subscription metadata: name: myevent-subscription spec: topic: wudi route: order/index pubsubname: rocketmq-pubsub scopes: - pushu1
其中需要特别关注以下几点:
• kind: 指定dapr Subscriptioon资源组件;
• topic: 消费者订阅的 topic 名称;
• route: 消费者代码 restful 接⼝;
• pubsubname: 指明消费者如何连接 RocketMQ,与第4步中关联 RocketMQ 的 name 保持一致;
• scopes: 作用域,pushu1 是 consumer 代码。
Step 6: 编写消费者代码逻辑(JAVA语言)
如下图所示,对比传统基于 RocketMQ SDK 编写的消费者代码,Dapr 代码中不包含任何 RocketMQ SDK 的内容。
其中的 order 和 index 和步骤5中的 route 相关联,当 wudi 中有消息生成时,Dapr会立即将消息发送到order/index。
基于Dapr编写消费者代码
基于RocketMQ SDK 编写的消费者代码
Step 7: 启动订阅者,对外服务端口为8080
运行 java -jar demo1-0.0.1-SNAPSHOT.jar
Step 8: 启动Daprd(自托管模式)
创建目录,并将步骤4&5中编写的2个资源文件保存在目录中。
mkdir -p /root/dapr/pubsub/rocketmq/yaml daprd -app-id pushu1 -components-path /root/dapr/pubsub/rocketmq/yaml -dapr-http-port 3015 --app-port 8080
在这段代码中:
• app-id: 消费者 app,要与步骤5中 scopes 的名字保持一致;
• components-path: 组件存放路径(两个yaml存放的目录);
• dapr-http-port: dapr对外暴露的 http 端口;
• app-port:dapr监听的消费者 app 端口;
启动 daprd,建立与 RocketMQ 和消费者 app 的关联。至此,Dapr 环境已经建立完成。
Step 9: 向 RocketMQ 投递消息,订阅者消费消息
向 rocketmq“wudi”topic中发送消息,同时消费者app立即收到消息并进行消费。
curl -X POST http://localhost:3015/v1.0/publish/rocketmq-pubsub/wudi -H "Content-Type: application/json" -d '{"data": "hello rocketmy & dapr" }'
对于基于 RocketMQ SDK 编写的生产者代码,Dapr 的编写方式更加简洁。需要注意的是这段请求代码要遵循 Dapr的格式。
基于RocketMQ SDK编写生产者代码
3、传统SDK方案对比Dapr方案
•生产者、消费者代码都不再依赖 RocketMQ SDK;
•业务和中间件完全解耦;
•业务开发人员只需关注业务逻辑,从过去学习 sdk api 用法到后续只需要 了解协议规范即可,比如:http://localhost:3015/v1.0/publish/rocketmq-p
因此,以发展的眼光看,新技术 Dapr 模式相比传统 SDK模式更加优秀,Dapr 模式让开发人员更专注于业务逻辑,而非业务逻辑的部分将会下沉到基础设施层面。
4、重新理解Dapr的定义
在讲解了 Dapr 模式后,再回看之前晦涩难懂的 Dapr 定义:
“Dapr是一个可移植的、事件驱动的运行时,它使任何开发人员能够轻松构建出弹性的、无状态和有状态的应用程序,并可运行在云平台或边缘计计算中,它同时也支持多种编程语和开发框架。”
我们再来看下这里的“运行时”,
运行时(runtime):程序正常执行时需要的支持:库、命令和环境等,其特征如下:
• runtime 是在程序之外,不由程序编写者提供;
• runtime 生命周期通常和程序生命周期关联;
RocketMQ 是运行时的一种具体实现,而 Dapr 是一堆运行时,如下图所示,RocketMQ 只是其中一个构建块“订阅和发布”(见红色框)的具体实现,而 Dapr 则负责多种构建块并解决中间件管理问题。
Dapr概貌
5、多运行时微服务架构
目前,微服务架构已经逐渐转化为基于云原生的分布式多运行时微服务架构,比如基于 K8S 的容器编排,基于Service Mesh 的网络流量管理,基于运行时微服务。这是一个全新的技术架构,希望大家可以更多的关注。
更多有关多运行时的微服务架构可以参考以下网址:https://www.infoq.com/articles/multi-runtime-microservice-architecture/
加入 Apache RocketMQ 社区
十年铸剑,Apache RocketMQ 的成长离不开全球接近 500 位开发者的积极参与贡献,相信在下个版本你就是 Apache RocketMQ 的贡献者,在社区不仅可以结识社区大牛,提升技术水平,也可以提升个人影响力,促进自身成长。
社区 5.0 版本正在进行着如火如荼的开发,另外还有接近 30 个 SIG(兴趣小组)等你加入,欢迎立志打造世界级分布式系统的同学加入社区,添加社区开发者微信:rocketmq666 即可进群,参与贡献,打造下一代消息、事件、流融合处理平台。
微信扫码添加小火箭进群
另外还可以加入钉钉群与 RocketMQ 爱好者一起广泛讨论:
钉钉扫码加群
往期推荐
RocketMQ-Streams 首个版本发布,轻量级计算的新选择
基于 RocketMQ Prometheus Exporter 打造定制化 DevOps 平台
平安保险基于 SPI 机制的 RocketMQ 定制化应用实践