首页 > 其他分享 >RocketMQ 全链路灰度探索与实践

RocketMQ 全链路灰度探索与实践

时间:2022-12-01 18:35:07浏览次数:57  
标签:消费 环境 服务端 过滤 灰度 消息 链路 RocketMQ

本文作者:肖京,Spring Cloud Alibaba PMC,阿里云智能技术专家。


01 全链路灰度背景介绍

RocketMQ 全链路灰度探索与实践_服务端

发布新版本时,为了有效、谨慎地验证新版本代码逻辑的正确性,通常会采用灰度发布,从而达到减小第一次变更影响面的目的。

举个例子,应用的集合中可能会包含交易中心、商品中心、库存中心等多个模块。在一次新版本发布的过程中,可能有 feature 既修改了交易中心,又修改了商品中心。为了验证新版本的正确性,需要让灰度的流量到达交易中心和商品中心的灰度版本,串联起来才能有效验证新版本的正确性,因此需要全链路灰度。

如上图,流量从入口应用进来之后,如果被识别成灰度流量,则它不仅会去往交易中心的灰度,也会去往商品中心的灰度。如果库存中心有灰度,也会去库存中心的灰度;如果没有,则会降级到库存中心的基线环境。

RocketMQ 全链路灰度探索与实践_基线_02

假设有一个网关,客户端可以从 iOS、安卓或 H5 对网关进行访问,在访问过程中,会给参数加上 header(http协议),header 头里包含用户 ID 信息。后端分为A、B、C三个模块,比如发布了新功能,需要更新 A 和 C 两个应用模块,A 和 C 的灰度版本需要同时发布,而B应用没有新特性发布。为了有效地验证 A 和 C 新版本代码逻辑的正确性,我们将灰度规则设置为user ID =120 时去往灰度环境。因此流量从网关进来之后,先去往 A 的灰度环境;而后A调用B时,发现 B 没有灰度环境,会降级到 B 的基线环境;下一跳 B 调用 C 的时候,发现 C 存在灰度环境,又重新回到 C 的灰度环境,以此实现全链路的灰度发布。

上述全链路灰度发布的过程可以有效验证 A 和 C 两个应用的新版本组合在一起时的有效性,只有可控范围内的流量参与到灰度环境,可以有效验证新版本的正确性,避免因为新版本的业务逻辑错误造成比较重大的损失或业务故障。

以上为RPC 层的全链路灰度。

RocketMQ 全链路灰度探索与实践_灰度_03

当链路请求中存在消息的时候,如何实现全链路灰度?

假设库存中心C收到一笔订单之后,会生产消息并发送到 RocketMQ server 中,同时由 A 应用来消费该消息。此时如果在过程中对消费逻辑进行了修改,则需要 C 应用生产的消息(灰度的消息)被 A 的灰度环境即RocketMQ的灰度消费者消费,才能实现灰度环境的闭环。


02 消息灰度的设计与实现

RocketMQ 全链路灰度探索与实践_基线_04

消息灰度的设计中,消息的生产者如何生产灰度消息?


带上灰度标签有以下三种方式:

①如果请求在入口被识别成灰度请求,则该消息会被标记成灰度消息。

②如果节点本身属于灰度节点,且开启了流量染色,则该消息会被标记为灰度消息。

③入口处请求没有被识别成灰度流量,但消息本身的 payload 属于灰度流量,则该消息也会被标记成灰度消息。


消息生产者在生产的时候,可以通过在 tag 或 user-property 中加上一些字段将灰度信息附带在消息体中。但是考虑到 tag 包含业务逻辑语义,且每个消息只能有一个 tag,因此不推荐使用 tag 。而 user-property 字段属于  key-value 结构,较灵活,更适用于存储消息的灰度标识。


RocketMQ 的 producer 中提供了 SendMessageHook,可以自定义逻辑,生产消息的时候可以将灰度标签存储在 user-property 中,消息发送到 RocketMQ server 的时候就包含了灰度信息。

RocketMQ 全链路灰度探索与实践_服务端_05

消费者灰度复杂一些,既支持客户端过滤,也支持服务端过滤。

从图中可以看到,开源的 RocketMQ client 中有 FilterMseeageHook 能够进行逻辑处理。可以通过 FilterMseeageHook 将本环境不需要消费的消息直接过滤掉。正式环境和灰度环境的消费者分别使用不同的 consumer group,使其 offset 分开。然后在 FilterMseeageHook 中加入对应逻辑,即可将灰度环境中收到所有非灰度的消息过滤掉。

正式环境需要拉取所有消息以分析 user-property 字段,key value 里包含消息的环境标,若识别到灰度环境的消息,则正式环境会通过 remove 的方式忽略此消息,灰度环境同理。

此套方案下,正式环境和灰度环境属于两个不同的 consumer group,且他们都需要将所有消息拉取本地。比较极端的场景下,比如灰度消费者只有一台机器,但是正式环境的消费者有 100 台机器,则灰度环境需要承担巨大压力。另一方面,RocketMQ server 服务端也需要将每条消息推送两次,也增加了服务端的压力。

客户端过滤的方式存在弊端,那么,能否通过服务端过滤来规避这些弊端?

服务端的过滤分为 Tag 过滤和 SQL92 过滤两种方式。

RocketMQ 全链路灰度探索与实践_灰度_06

RocketMQ 的服务端过滤包含两种过滤模式,分为 Tag 过滤和 SQL92 过滤。

Tag 过滤的实现中,RocketMQ 消费者向 server 端订阅的时候,会传递订阅信息到服务端。订阅信息为 SubscribtionData,其中包含四个字段:

  • topic
  • tagSet
  • expressinotallow=tag:表达式的类型,这里是 tag 过滤,所以值为tag
  • client version:此次订阅的版本号

Client 会不断向 server 端发送心跳,默认情况下 30 秒一次。过程中  SubscribtionData 可以动态变化,如果对tagSet或表达式的类型进行过更改,则会增加 client version 的值。服务端收到心跳之后,发现心跳里的 SubscribtionData 版本号改变,意味着订阅规则也有所变化,此时会更新客户端的订阅逻辑,决定服务端过滤变化的推送。


Server 端处理服务端的灰度过滤逻辑如下:

RocketMQ 中有一个 MessageFilter 类,首先会进行 consumer queue 的比对,如果匹配成功,则进行 tagscode 的比对。两次比对都匹配才会将消息推送到 client 消费者端。


以上流程的优势为避免了灰度环境拉取所有消息,能够有效减轻灰度环境消费者的负担。同时,服务端不会将所有消息都推送两遍,大大降低了服务端压力。

RocketMQ 全链路灰度探索与实践_灰度_07

如果灰度信息保存在 user-property 字段,可以通过 SQL 92 的方式进行过滤。

服务端 ConsumerFilterManager 保存了每一个 topic 对应的FilterDataMapByTopic,而 FilterDataMapByTopic 里保存了不同 consumer group 对应的消费逻辑ConsumerFilterData,ConsumerFilterData 里包含了 consumer group、topic、表达式以及 client version,与客户端发送的信息非常类似,因此可以借此来过滤。

SQL 92 是一种可以写复杂表达式的过滤规则,除了能够实现tag 过滤方式,也能基于 user-property 字段进行过滤。

RocketMQ 全链路灰度探索与实践_服务端_08

SQL92 的过滤规则如上图所示。

  • 消费 Tag 为 A 或者 Tag 为 B 的消息可以写为:(TAGS is not null and TAGS in ('A', 'B'))
  • 消费 user-property 中 version 为 gray 的消息可以写为:(version is 'gray')
  • 消费 Tag 为 A 且 user-property 中 version 为 gray 的消息可以写为:(TAGS is  'A') and (version is 'gray')
  • 消费 Tag 为 A 且 user-property 中 version 为 green 或者 blue 的消息可以写为:(TAGS is not null and TAGS is  'A' ) and ( (version is 'green') or (version is 'blue') )

RocketMQ 全链路灰度探索与实践_基线_09

此外,实际应用场景中要实现完善的消息灰度,还有诸多需要考虑的问题:


  • 如果消息生产逻辑是独立的线程池,如何实现灰度标的透传?常见方式为往线程池里提交任务的时候,将属于灰度的标志放到 task 某个字段中,在消费的时候读取 task 字段,重新放在 thread local 中,实现跨线程的灰度标传递。
  • 全路灰度发布的过程中出现了回滚,有一些灰度消息是否会出现没有灰度消费者的情况?消息一直没有被消费,应该怎么办?有两种选择,一种是筛选没有消费的消息,进行补偿动作;如果消息的消费逻辑没有进行较大变更,也可以容忍让基线环境消费灰度消息。
  • 消息灰度过程中出现重复消费怎么办?可以在消费逻辑中保证幂等,或设置更精准的灰度控制逻辑。
  • 消费者的订阅行为能否支持动态变更?比如没有灰度的消费者是否可以用正式环境消费灰度消息?
  • 如果正式环境可以消费灰度消息,那么正式环境的默认行为时消费所有消息,还是只消费正式环境的消息?
  • 如何实现自定义的消息灰度逻辑?比如流量刚进来的时候,并没有被识别为灰度流量,但是在发送过程中发现消息里有一些特殊的逻辑,正好命中了灰度规则,则需要将其标记为一条灰度规则。此时如何做自定义的灰度逻辑?
  • 假设正式环境能够消费灰度的消息,过程中如果灰度的消费者启动了,正式环境能否自动探测是否需要消费灰度?实现这一点可以避免重复消费,同时又能解决发布上下游有联动的情况。


03 MSE全链路灰度最佳实践

RocketMQ 全链路灰度探索与实践_服务端_10

阿里云的微服引擎MSE的全链路灰度最佳实践如上图:name=xiaoming 属于灰度流量,进行规则配置后流量经过 A 的灰度环境再到 B 的基线环境再到 C 的灰度环境,C 产生的灰度消息可以被 RocketMQ Server 接收,同时准确推送到 A 的灰度环境,实现闭环。

RocketMQ 全链路灰度探索与实践_服务端_11

只要基于开源标准的方式开发,接入使用MSE就不需要修改任何代码,业务无侵入/无感知,而且零升级成本,完全不需要改变现有的业务架构。主要通过 One Java Agent 方式实现,通过 Java Agent 进行字节码的增强,使消息的生产者和消费者在开源RocketMQ 的发送行为和消费行为自动注入灰度相关字节码,业务部署以后即可自动 attach 上 Java Agent,无需修改任何逻辑就能完成消息灰度。

上图中,消息生产者挂载上 Java Agent 之后,agent 会自动修改发送消息的逻辑。如果识别到消息属于灰度流量,会自动将消息加上灰度标,发送到消息服务端。消息消费者在启动过程中识别到灰度环境,会自动通过 Java Agent 修改 consumer group ,而且发送订阅规则时会自动订阅只消费灰度的消息。消息的服务端会对过滤的规则进行识别和匹配,以保证只有灰度消息会推送给灰度消费者。

接入微服务引擎 MSE 后,所有对于消息灰度的操作可直接在控制台上完成。用户只需在 MSE 控制台进行治理规则的配置,即可自动通过配置中心给消费者推送消费规则,使消费者可以动态变更消费行为而完成消息灰度的完整过程。过程中无需修改任何代码,只需要接入 Java Agent 即可实现全部功能。

RocketMQ 全链路灰度探索与实践_服务端_12

使用步骤如下:在 MSE 的应用列表页选择对应的应用,开启消息灰度,配置基线环境和需要忽略的标签,即可使用消息的灰度。

点击链接可查看更多相关信息:

​https://help.aliyun.com/document_detail/397318.html​

RocketMQ 全链路灰度探索与实践_服务端_13

接入MSE后,Demo的效果为:A 应用的灰度环境只会消费灰度消息,基线环境只会消费基线消息。

RocketMQ 全链路灰度探索与实践_灰度_14

上图为Demo效果。左边为基线环境,只会消费基线的应用生产出来的消息,后续A 调 B 调 C 的过程中也只会在基线的环境。右边日志是 A 的灰度环境,消费的消息用 C 的灰度环境生产出来。往后调用 A 调 B 调 C 的过程中,A是灰度环境,调用 B 的时候,因B只有基线环境,所以是流量消息只到B的基线环境,最后到达 C 的灰度环境。消费消息的时候,能够继续带上消息的灰度标往下透传并走到正确路径上。

Demo源码下载地址:

​https://github.com/aliyun/alibabacloud-microservice-demo/tree/master/mse-simple-demo​


标签:消费,环境,服务端,过滤,灰度,消息,链路,RocketMQ
From: https://blog.51cto.com/u_15870054/5903560

相关文章

  • 基于 RocketMQ 的 Dubbo-go 通信新范式
    本文作者:郝洪范,Dubbo-goCommitter,京东资深研发工程师。一、MQRequestReply特性介绍什么是RPC通信?如上图所示,类似于本地调用,A服务响应调用B服务的helloworld......
  • RocketMQ 全链路灰度探索与实践
    本文作者:肖京,SpringCloudAlibabaPMC,阿里云智能技术专家。01全链路灰度背景介绍发布新版本时,为了有效、谨慎地验证新版本代码逻辑的正确性,通常会采用灰度发布,从而达......
  • RocketMQ顺序消息
    抛出问题顺序消息?不是消息队列么,队列不是有序的么,不是先进先出么,对,队列是的,但是RocketMQ是对队列的升级,我们创建一个topic其实就是相当于创建了一个队列,但是这个队列是聚......
  • can数据链路层汇总
    一,标准帧1,帧起始,SOF,2,仲裁段,ID,RTR3,控制段,DLC,STR4,数据段,DBC解析5,CRC段,6,ACK段,7,帧结束二,远程帧没有数据段的标准帧  三,仲裁机制,非破坏性,类似堵塞式SOCKET ......
  • filter 灰度处理:公祭日,一行代码让页面变成黑白色调
    我们这里通过打开Google浏览器调试模式来实现快速预览。首先在Elements里面点击body标签,在右侧出现的功能栏里找到style属性,具体点击位置如下图所示:这里我们将......
  • RocketMQ系列-搭建Namesrv源码调试环境
    RocketMQ系列-搭建Namesrv源码调试环境在学习任何一个技术框架的时候,我们通常都是先了解是什么,有什么作用、解决什么问题、设计亮点和设计思想是什么;当然对于技术学习上来......
  • 原生Ingress灰度发布能力不够?我们是这么干的
    灰度发布是一种常见的服务滚动升级或A/B测试策略。在新版本服务正式发布前,可以部署少量的新版本服务和上个版本共存,用部分生产流量测试新版本的功能和特性。如果新版本反......
  • RocketMQ 的消费者类型详解与最佳实践
    作者:凌楚在RocketMQ5.0中,更加强调了客户端类型的概念,尤其是消费者类型。为了满足多样的RocketMQ中一共有三种不同的消费者类型,分别是PushConsumer、SimpleConsumer和......
  • RocketMQ 的消费者类型详解与最佳实践
    作者:凌楚在RocketMQ5.0中,更加强调了客户端类型的概念,尤其是消费者类型。为了满足多样的RocketMQ中一共有三种不同的消费者类型,分别是PushConsumer、SimpleConsumer......
  • 构建基于 Ingress 的全链路灰度能力
    作者:涂鸦背景随着云原生技术不断普及,越来越多的业务应用开始向云原生架构转变,借助容器管理平台Kubernetes的不可变基础设施、弹性扩缩容和高扩展性,助力业务迅速完成数......