RocketMQ 全链路灰度探索与实践
本文作者:肖京,Spring Cloud Alibaba PMC,阿里云智能技术专家。
01 全链路灰度背景介绍
发布新版本时,为了有效、谨慎地验证新版本代码逻辑的正确性,通常会采用灰度发布,从而达到减小第一次变更影响面的目的。
举个例子,应用的集合中可能会包含交易中心、商品中心、库存中心等多个模块。在一次新版本发布的过程中,可能有 feature 既修改了交易中心,又修改了商品中心。为了验证新版本的正确性,需要让灰度的流量到达交易中心和商品中心的灰度版本,串联起来才能有效验证新版本的正确性,因此需要全链路灰度。
如上图,流量从入口应用进来之后,如果被识别成灰度流量,则它不仅会去往交易中心的灰度,也会去往商品中心的灰度。如果库存中心有灰度,也会去库存中心的灰度;如果没有,则会降级到库存中心的基线环境。
假设有一个网关,客户端可以从 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 层的全链路灰度。
当链路请求中存在消息的时候,如何实现全链路灰度?
假设库存中心C收到一笔订单之后,会生产消息并发送到 RocketMQ server 中,同时由 A 应用来消费该消息。此时如果在过程中对消费逻辑进行了修改,则需要 C 应用生产的消息(灰度的消息)被 A 的灰度环境即RocketMQ的灰度消费者消费,才能实现灰度环境的闭环。
02 消息灰度的设计与实现
消息灰度的设计中,消息的生产者如何生产灰度消息?
带上灰度标签有以下三种方式:
①如果请求在入口被识别成灰度请求,则该消息会被标记成灰度消息。
②如果节点本身属于灰度节点,且开启了流量染色,则该消息会被标记为灰度消息。
③入口处请求没有被识别成灰度流量,但消息本身的 payload 属于灰度流量,则该消息也会被标记成灰度消息。
消息生产者在生产的时候,可以通过在 tag 或 user-property 中加上一些字段将灰度信息附带在消息体中。但是考虑到 tag 包含业务逻辑语义,且每个消息只能有一个 tag,因此不推荐使用 tag 。而 user-property 字段属于 key-value 结构,较灵活,更适用于存储消息的灰度标识。
RocketMQ 的 producer 中提供了 SendMessageHook,可以自定义逻辑,生产消息的时候可以将灰度标签存储在 user-property 中,消息发送到 RocketMQ server 的时候就包含了灰度信息。
消费者灰度复杂一些,既支持客户端过滤,也支持服务端过滤。
从图中可以看到,开源的 RocketMQ client 中有 FilterMseeageHook 能够进行逻辑处理。可以通过 FilterMseeageHook 将本环境不需要消费的消息直接过滤掉。正式环境和灰度环境的消费者分别使用不同的 consumer group,使其 offset 分开。然后在 FilterMseeageHook 中加入对应逻辑,即可将灰度环境中收到所有非灰度的消息过滤掉。
正式环境需要拉取所有消息以分析 user-property 字段,key value 里包含消息的环境标,若识别到灰度环境的消息,则正式环境会通过 remove 的方式忽略此消息,灰度环境同理。
此套方案下,正式环境和灰度环境属于两个不同的 consumer group,且他们都需要将所有消息拉取本地。比较极端的场景下,比如灰度消费者只有一台机器,但是正式环境的消费者有 100 台机器,则灰度环境需要承担巨大压力。另一方面,RocketMQ server 服务端也需要将每条消息推送两次,也增加了服务端的压力。
客户端过滤的方式存在弊端,那么,能否通过服务端过滤来规避这些弊端?
服务端的过滤分为 Tag 过滤和 SQL92 过滤两种方式。
RocketMQ 的服务端过滤包含两种过滤模式,分为 Tag 过滤和 SQL92 过滤。
Tag 过滤的实现中,RocketMQ 消费者向 server 端订阅的时候,会传递订阅信息到服务端。订阅信息为 SubscribtionData,其中包含四个字段:
- topic
- tagSet
- expressionType=tag:表达式的类型,这里是 tag 过滤,所以值为tag
- client version:此次订阅的版本号
Client 会不断向 server 端发送心跳,默认情况下 30 秒一次。过程中 SubscribtionData 可以动态变化,如果对tagSet或表达式的类型进行过更改,则会增加 client version 的值。服务端收到心跳之后,发现心跳里的 SubscribtionData 版本号改变,意味着订阅规则也有所变化,此时会更新客户端的订阅逻辑,决定服务端过滤变化的推送。
Server 端处理服务端的灰度过滤逻辑如下:
RocketMQ 中有一个 MessageFilter 类,首先会进行 consumer queue 的比对,如果匹配成功,则进行 tagscode 的比对。两次比对都匹配才会将消息推送到 client 消费者端。
以上流程的优势为避免了灰度环境拉取所有消息,能够有效减轻灰度环境消费者的负担。同时,服务端不会将所有消息都推送两遍,大大降低了服务端压力。
如果灰度信息保存在 user-property 字段,可以通过 SQL 92 的方式进行过滤。
服务端 ConsumerFilterManager 保存了每一个 topic 对应的FilterDataMapByTopic,而 FilterDataMapByTopic 里保存了不同 consumer group 对应的消费逻辑ConsumerFilterData,ConsumerFilterData 里包含了 consumer group、topic、表达式以及 client version,与客户端发送的信息非常类似,因此可以借此来过滤。
SQL 92 是一种可以写复杂表达式的过滤规则,除了能够实现tag 过滤方式,也能基于 user-property 字段进行过滤。
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') )
此外,实际应用场景中要实现完善的消息灰度,还有诸多需要考虑的问题:
如果消息生产逻辑是独立的线程池,如何实现灰度标的透传?常见方式为往线程池里提交任务的时候,将属于灰度的标志放到 task 某个字段中,在消费的时候读取 task 字段,重新放在 thread local 中,实现跨线程的灰度标传递。
全路灰度发布的过程中出现了回滚,有一些灰度消息是否会出现没有灰度消费者的情况?消息一直没有被消费,应该怎么办?有两种选择,一种是筛选没有消费的消息,进行补偿动作;如果消息的消费逻辑没有进行较大变更,也可以容忍让基线环境消费灰度消息。
消息灰度过程中出现重复消费怎么办?可以在消费逻辑中保证幂等,或设置更精准的灰度控制逻辑。
消费者的订阅行为能否支持动态变更?比如没有灰度的消费者是否可以用正式环境消费灰度消息?
如果正式环境可以消费灰度消息,那么正式环境的默认行为时消费所有消息,还是只消费正式环境的消息?
如何实现自定义的消息灰度逻辑?比如流量刚进来的时候,并没有被识别为灰度流量,但是在发送过程中发现消息里有一些特殊的逻辑,正好命中了灰度规则,则需要将其标记为一条灰度规则。此时如何做自定义的灰度逻辑?
假设正式环境能够消费灰度的消息,过程中如果灰度的消费者启动了,正式环境能否自动探测是否需要消费灰度?实现这一点可以避免重复消费,同时又能解决发布上下游有联动的情况。
03 MSE全链路灰度最佳实践
阿里云的微服引擎MSE的全链路灰度最佳实践如上图:name=xiaoming 属于灰度流量,进行规则配置后流量经过 A 的灰度环境再到 B 的基线环境再到 C 的灰度环境,C 产生的灰度消息可以被 RocketMQ Server 接收,同时准确推送到 A 的灰度环境,实现闭环。
只要基于开源标准的方式开发,接入使用MSE就不需要修改任何代码,业务无侵入/无感知,而且零升级成本,完全不需要改变现有的业务架构。主要通过 One Java Agent 方式实现,通过 Java Agent 进行字节码的增强,使消息的生产者和消费者在开源RocketMQ 的发送行为和消费行为自动注入灰度相关字节码,业务部署以后即可自动 attach 上 Java Agent,无需修改任何逻辑就能完成消息灰度。
上图中,消息生产者挂载上 Java Agent 之后,agent 会自动修改发送消息的逻辑。如果识别到消息属于灰度流量,会自动将消息加上灰度标,发送到消息服务端。消息消费者在启动过程中识别到灰度环境,会自动通过 Java Agent 修改 consumer group ,而且发送订阅规则时会自动订阅只消费灰度的消息。消息的服务端会对过滤的规则进行识别和匹配,以保证只有灰度消息会推送给灰度消费者。
接入微服务引擎 MSE 后,所有对于消息灰度的操作可直接在控制台上完成。用户只需在 MSE 控制台进行治理规则的配置,即可自动通过配置中心给消费者推送消费规则,使消费者可以动态变更消费行为而完成消息灰度的完整过程。过程中无需修改任何代码,只需要接入 Java Agent 即可实现全部功能。
使用步骤如下:在 MSE 的应用列表页选择对应的应用,开启消息灰度,配置基线环境和需要忽略的标签,即可使用消息的灰度。
点击链接可查看更多相关信息:
https://help.aliyun.com/document_detail/397318.html
接入MSE后,Demo的效果为:A 应用的灰度环境只会消费灰度消息,基线环境只会消费基线消息。
上图为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 全链路灰度探索与实践的更多相关文章
- 基于 Istio 的全链路灰度方案探索和实践
作者|曾宇星(宇曾) 审核&校对:曾宇星(宇曾) 编辑&排版:雯燕 背景 微服务软件架构下,业务新功能上线前搭建完整的一套测试系统进行验证是相当费人费时的事,随着所拆分出微服务数量的不 ...
- Opentracing + Uber Jaeger 全链路灰度调用链,Nepxion Discovery
当网关和服务在实施全链路分布式灰度发布和路由时候,我们需要一款追踪系统来监控网关和服务走的是哪个灰度组,哪个灰度版本,哪个灰度区域,甚至监控从Http Header头部全程传递的灰度规则和路由策略.这 ...
- 基于Opentracing+Jaeger全链路灰度调用链
当网关和服务在实施全链路分布式灰度发布和路由时候,我们需要一款追踪系统来监控网关和服务走的是哪个灰度组,哪个灰度版本,哪个灰度区域,甚至监控从Http Header头部全程传递的灰度规则和路由策略.这 ...
- 高德全链路压测平台TestPG的架构与实践
导读 2018年十一当天,高德DAU突破一个亿,不断增长的日活带来喜悦的同时,也给支撑高德业务的技术人带来了挑战.如何保障系统的稳定性,如何保证系统能持续的为用户提供可靠的服务?是所有高德技术人面临的 ...
- 让全链路压测变得更简单!Takin2.0重磅来袭!
自Takin社区版1.0发布两个多月以来,有很多测试同学陆续在各自的工作中运用了起来,其中包括金融.电商.物流.出行服务等行业.这个过程中我们收到了很多同学的反馈建议,同时也了解到很多同学在落地全链路 ...
- 全链路压测平台(Quake)在美团中的实践
背景 在美团的价值观中,以“客户为中心”被放在一个非常重要的位置,所以我们对服务出现故障越来越不能容忍.特别是目前公司业务正在高速增长阶段,每一次故障对公司来说都是一笔非常不小的损失.而整个IT基础设 ...
- 全链路实践Spring Cloud 微服务架构
Spring Cloud 微服务架构全链路实践Spring Cloud 微服务架构全链路实践 阅读目录: 网关请求流程 Eureka 服务治理 Config 配置中心 Hystrix 监控 服务调用链 ...
- 从自动化到智能化,网易杭研的AIOps探索与实践
在大数据时代下,我们借助机器学习.数据仓库.大数据平台等大数据技术手段,将运维产生的数据进行分析.处理,得出最佳运维策略,以期实现对故障的事先干预,将风险降低到最低,从而降低运维成本,提升运维效率,最 ...
- zz深度学习在美团配送 ETA 预估中的探索与实践
深度学习在美团配送 ETA 预估中的探索与实践 比前一版本有改进: 基泽 周越 显杰 阅读数:32952019 年 4 月 20 日 1. 背景 ETA(Estimated Time of A ...
- 深度学习在美团配送ETA预估中的探索与实践
1.背景 ETA(Estimated Time of Arrival,“预计送达时间”),即用户下单后,配送人员在多长时间内将外卖送达到用户手中.送达时间预测的结果,将会以”预计送达时间”的形式,展现 ...
随机推荐
- (计算机类)人工智能方向会议的截止时间表 —— AI Conference Deadlines —— 会议投稿截止时间
由 https://paperswithcode.com/ 提供的时间表. 做AI方向的research,经常需要关注的就是conference的deadline,之前往往都是需要手动的去挨个搜索,下 ...
- java多线程之-CAS无锁
1.背景 加锁确实能解决线程并发的的问题,但是会造成线程阻塞等待等问题 那么有没有一种方法,既可以线程安全,又不会造成线程阻塞呢? 答案是肯定的......请看如下案例 注意:重要的文字说明,写在了代 ...
- 社区6月月报 | Apache SeaTunnel重要更新与优化记录
各位热爱Apache SeaTunnel的小伙伴们,社区6月份月报来啦!这里将记录Apache SeaTunnel社区每月的重要更新,欢迎关注. 月度Merge Stars 感谢以下小伙伴上个月为Ap ...
- 第 111 场双周赛 - 力扣(LeetCode)
第 111 场双周赛 - 力扣(LeetCode) 2824. 统计和小于目标的下标对数目 - 力扣(LeetCode) 枚举即可 class Solution { public: int count ...
- SMU Summer 2023 Contest Round 12
SMU Summer 2023 Contest Round 12 A. K-divisible Sum 分类讨论: \(n > k\)时: \(n\)能整除\(k\)就全是\(1\) 不能整除, ...
- 【CMake系列】03-cmake 注释、常用指令 message、set、file、for_each、流程控制if
本文给出了 cmake 中的 一些常用的 指令,可以快速了解,为后面的内容深入 打点基础. 本专栏的详细实践代码全部放在 github 上,欢迎 star !!! 如有问题,欢迎留言.或加群[3927 ...
- Ubuntu 设置 SMB 服务
安装 Samba 包 sudo apt install samba samba-common 创建用于 SMB 共享的文件夹 sudo mkdir /usr/local/volumes # 新建用于共 ...
- 【测试平台开发】——01后端web开发框架Flask
官方中文地址:https://flask.net.cn/ 官方英文地址:https://flask.palletsprojects.com/en/2.1.x/ github地址:https://git ...
- Java Swing Loading转圈的进度提示框
Java Swing Loading转圈的进度提示框 具体只需要两个类 AnimatedPanel.java InfiniteProgressPanel.java 前因:我们开发的web应用,有个奇葩 ...
- Codeforces Round 916 (Div. 3) (A~F附带题解和详细思路)
Codeforces Round 916 (Div. 3) (A~E2) A. Problemsolving Log 签到题,对于给出的字符串,用数组记录每个字母出现的次数,然后遍历一边记录数组,如果 ...