【实战】RocketMQ消息灰度方案-消息逻辑隔离

718 阅读18分钟

全链路灰度之消息灰度

背景与场景

灰度缺陷

消息隔离的原则

与RPC隔离的原则一样,异曲同工之妙

  1. 灰度消费者,只能消费灰度消息(特定标签);
  2. 默认正常消费者,可以同时消费正常消息灰度消息(自动识别),灰度消息可以在灰度和正常消费者之间穿梭;(兜底)

消息消费者的行为如下:

  • 未打标的环境节点默认会消费所有环境生产出来的消息
  • 打标的环境节点只消费在相同标签环境生产出来的消息

灰度场景

大部分场景下 MQ 的灰度并不会像 RPC 那样那么严格,但是我们需要确认消费场景,即当灰度消费者不存在的情况下,消息是否应该由正常消费者去消费。 灰度场景

1.灰度消息只由灰度节点消费

事实的情况是可能大家都想要这种严格意义上的消息灰度隔离策略,由此才证明是真正的消息灰度方案,但是这个方案需要考虑一些具体场景问题。

比如,有时候作为灰度节点的发送方,它的功能改动点并不是在 MQ 这块,但是它发送的消息却是灰度消息,而消息的消费方可能也未发生过功能变动,也不会有与之对应的灰度消费标识,这种情况下如果我们将灰度的消息进行丢弃的话,那么会造成最终的数据不完整。

2.灰度消息可以由正常节点消费

因此,我们再考虑第二种方案,如果当灰度消费节点不存在时,消息会由正常节点消费,当存在灰度节点时,则由灰度节点消费,正常节点消费灰度消息只为了当灰度节点不存在时的兜底。

那么,这种场景仍然可能存在问题,比如当消费节点的消费逻辑发生改变时,由正常节点消费就可能造成业务上的错误。对于此问题我们可以默认认为如果消费方发生逻辑改变,则灰度节点大概率一定是存在的,如果一些异常情况导致的异常或者宕机的场景,仍然能通过人工或者告警判断出来,总之,这个问题认为不算是问题。

消息灰度方案

逻辑隔离(ConsumerGroup+环境标签过滤) vs 物理隔离(影子Topic)

使用Apache RocketMQ开源最新稳定版本

消息灰度方案,请问有没轻量级的逻辑隔离方案?

类似Dubbo的标签路由,通过标签实现流量隔离环境(灰度、多套开发环境等)。

常见消息灰度方案

常规的灰度方案一般都会选择不同的消费组,处理方式有影子Topic、Tag过滤以及UserProperty过滤

影子Topic是物理存储层面的隔离方案,通过主题实现存储的隔离性和订阅隔离性。我的理解比较重,很难打通正常消费组和灰度消费组,即正常消费组很难消费灰度消息。

全链路灰度发布

全链路灰度中的消息灰度

消息灰度的设计与实现

生产者灰度: 带上灰度标签

如何标记灰度消息,Tag 还是 user-property ? 有时候 tag 会有业务语义,而且每个消息只能有一个 tag。

而 user-property 是 key-value 的结构,比较灵活,所以推荐使用 user-property 来存储灰度标识。

消费者灰度: 客户端本地过滤

客户端过滤方式是最简单和有效的方式。正式环境和灰度环境使用不同的 consumer group,只需要在 FilterMessageHook 中增加对应的过滤逻辑,灰度环境过滤掉所有非灰度的消息,正式环境过滤掉所有非正式的消 息,就这么简单的完成了消费者这一层的灰度的逻辑。

但是这套方案下,正式环境和灰度环境都需要拉取所有的消息,会造成灰度环境的压力比较大,而且服务端也需要把所有的消息都推送两次,这也增加了服务端的压力。

消费者灰度: 服务端 Tag 过滤

服务端灰度,避免了灰度环境拉取所有的消息,减轻了灰度环境消费者负担,也降低了服务端的压力。

消费者灰度: 服务端 SQL92 过滤

阿里云微服务引擎MSE的消息灰度方案

流量治理>配置消息灰度 - 微服务引擎

逻辑隔离(ConsumerGroup + BrokerServer的UserProperty过滤投递)

优势:在BrokerServer统一处理与配置,管控与治理都好用 劣势:消息灰度和QL92过滤方式需要使用铂金版的MQ

如果您在使用金丝雀发布全链路灰度以及开发环境隔离等场景中需要使用到消息的灰度,那么您需要开启消息灰度的功能。目前,MSE只支持RocketMQ类型的消息灰度。

背景信息

背景信息

使用说明

  • 消息类型目前只支持RocketMQ,包含开源版本和阿里云商业版。

    • 如果您使用开源RocketMQ,则RocketMQ Server和RocketMQ Client都需要使用4.5.0及以上版本。(开源版本直接可以使用)
  • 开启消息灰度后,MSE会修改消息的Consumer Group。例如原来的Consumer Group为group1,环境标签为gray,开启消息灰度后,则group1会被修改成group1_gray,如果您使用的是阿里云RocketMQ ,请提前创建好group1。

  • 默认使用SQL92的过滤方式,如果您使用的是开源RocketMQ,开源的RocketMQ Server端需要支持SQL92过滤,且在服务端开启此功能(即在broker.conf中配置enablePropertyFilter=true)。

如果您的应用场景不满足支持SQL92过滤的条件,那么可以使用通过FilterMessageHook在消费者过滤的方式,此方式需要在所有的应用中打开消息灰度并且选择客户端过滤方式。因为消费者过滤的方式会在每个环境都处理全量的消息,对消息的生产者和消费者压力都比较大,不推荐在生产中使用此模式

使用说明

开启消息灰度

  • 未打标环境忽略的标签右侧单击编辑,打开开启消息灰度右侧的开关,然后单击确定。
    • 如果您不希望未打标环境消费其他环境生产出来的消息,请在未打标环境忽略的标签中选择需要忽略的标签。(类似于消费者使用消息标签订阅)
    • 如果您希望基线环境能够智能地识别其他环境是否存在消费者在不存在消费者时消费对应环境的消息,在存在消费者时忽略对应环境的消息,请在未打标环境忽略的标签中输入自动识别
  • 当消息的生产者和消费者都开启消息灰度,并且都重启生效之后。消息消费者的行为如下:
    • 未打标的环境节点默认会消费所有环境生产出来的消息
    • 打标的环境节点只消费在相同标签环境生产出来的消息

开启消息灰度

MSE 全链路灰度最佳实践

  • 消息灰度需要您使用铂金版的MQ
  • SQL92过滤方式也是需要铂金版的

消息隔离想法

与阿里云微服务引擎MSE的消息灰度方案是类似想法

逻辑隔离(ConsumerGroup + Consumer的标签订阅SQL Tag过滤消费)

优势:在生产者端实现隔离,所有消费者分组等元数据信息都在Broker服务端,消费者端不需要改动。

劣势:在消费者端实现隔离,基线消费者需要知道所有消费者分组信息,为Tag订阅做辅助决策。

消费组使用不同的GroupID,即${consumerGroup_Tag} + SQL Tag过滤,rocketmq-client具有消费者元数据的注册订阅发现能力,类似Dubbo的服务发现能力。

这样消息就能在正常和灰度消费者之间来回穿梭,真正做到逻辑隔离。

参考

RocketMQ领域模型概述

rocketmq.apache.org/zh/docs/dom…

Apache RocketMQ 是一款典型的分布式架构下的中间件产品,使用异步通信方式发布订阅的消息传输模型。通信方式和传输模型的具体说明,请参见下文通信方式介绍和消息传输模型介绍。 Apache RocketMQ 产品具备异步通信的优势,系统拓扑简单、上下游耦合较弱,主要应用于异步解耦,流量削峰填谷等场景

Apache RocketMQ领域模型

RocketMQ领域模型 如上图所示,Apache RocketMQ 中消息的生命周期主要分为消息生产、消息存储、消息消费这三部分。

生产者生产消息发送至 Apache RocketMQ 服务端消息存储服务端主题中,消费者通过订阅主题消费消息

消息生产

生产者(Producer)

Apache RocketMQ 中用于产生消息的运行实体,一般集成于业务调用链路的上游。生产者是轻量级匿名无身份的

消息存储

主题(Topic)

Apache RocketMQ 消息传输和存储的分组容器主题内部由多个队列组成消息的存储和水平扩展实际是通过主题内的队列实现的。

队列(MessageQueue)

Apache RocketMQ 消息传输和存储的实际单元容器,类比于其他消息队列中的分区。Apache RocketMQ 通过流式特性的无限队列结构存储消息消息在队列内具备顺序性存储特征

消息(Message)

Apache RocketMQ 的最小传输单元。消息具备不可变性,在初始化发送和完成存储后即不可变。

消息消费

消费者分组(ConsumerGroup)

Apache RocketMQ 发布订阅模型中定义的独立的消费身份分组,用于统一管理底层运行的多个消费者(Consumer)同一个消费组的多个消费者必须保持消费逻辑和配置一致,共同分担该消费组订阅的消息,实现消费能力的水平扩展

消费者(Consumer)

Apache RocketMQ 消费消息的运行实体,一般集成在业务调用链路的下游。消费者必须被指定到某一个消费组中

订阅关系(Subscription)

Apache RocketMQ 发布订阅模型中消息过滤、重试、消费进度的规则配置订阅关系以消费组粒度进行管理消费组通过定义订阅关系控制指定消费组下的消费者如何实现消息过滤、消费重试及消费进度恢复等

Apache RocketMQ 的订阅关系除过滤表达式之外都是持久化的,即服务端重启或请求断开,订阅关系依然保留。

通信方式介绍

异步通信模型 异步通信模型 异步消息通信模式下,各子系统之间无需强耦合直接连接调用方只需要将请求转化成异步事件(消息)发送给中间代理,发送成功即可认为该异步链路调用完成,剩下的工作中间代理会负责将事件可靠通知到下游的调用系统,确保任务执行完成。该中间代理一般就是消息中间件。

异步通信的优势如下:

  • 系统拓扑简单。由于调用方和被调用方统一和中间代理通信,系统是星型结构易于维护和管理

  • 上下游耦合性弱。上下游系统之间弱耦合,结构更灵活,由中间代理负责缓冲和异步恢复。 上下游系统间可以独立升级和变更,不会互相影响。

  • 容量削峰填谷。基于消息的中间代理往往具备很强的流量缓冲和整形能力,业务流量高峰到来时不会击垮下游。

消息传输模型介绍

发布订阅模型 发布订阅模型 发布订阅模型具有如下特点

  • 消费独立:相比队列模型的匿名消费方式,发布订阅模型中消费方都会具备的身份,一般叫做订阅组(订阅关系),不同订阅组之间相互独立不会相互影响。

  • 一对多通信:基于独立身份的设计同一个主题内的消息可以被多个订阅组处理,每个订阅组都可以拿到全量消息。因此发布订阅模型可以实现一对多通信。

主题(Topic)

rocketmq.apache.org/zh/docs/dom…

定义

主题是 Apache RocketMQ 中消息传输和存储的顶层容器用于标识同一类业务逻辑的消息主题的作用主要如下:

  • 定义数据的分类隔离: 在 Apache RocketMQ 的方案设计中,建议将不同业务类型的数据拆分到不同的主题中管理,通过主题实现存储的隔离性和订阅隔离性

  • 定义数据的身份和权限: Apache RocketMQ 的消息本身是匿名无身份的,同一分类的消息使用相同的主题来做身份识别和权限管理

订阅关系(Subscription)

rocketmq.apache.org/zh/docs/dom…

定义

订阅关系是 Apache RocketMQ 系统中消费者获取消息、处理消息的规则和状态配置

订阅关系由消费者分组动态注册到服务端系统,并在后续的消息传输中按照订阅关系定义的过滤规则进行消息匹配和消费进度维护

通过配置订阅关系,可控制如下传输行为

  • 消息过滤规则:用于控制消费者在消费消息时,选择主题内的哪些消息进行消费,设置消费过滤规则可以高效地过滤消费者需要的消息集合,灵活根据不同的业务场景设置不同的消息接收范围。具体信息,请参见消息过滤

  • 消费状态:Apache RocketMQ 服务端默认提供订阅关系持久化的能力,即消费者分组在服务端注册订阅关系后,当消费者离线并再次上线后,可以获取离线前的消费进度并继续消费

订阅关系判断原则

Apache RocketMQ 的订阅关系按照消费者分组主题粒度设计,因此,一个订阅关系指的是指定某个消费者分组对于某个主题的订阅判断原则如下:

  • 不同消费者分组对于同一个主题的订阅相互独立。如下图所示,消费者分组Group A和消费者分组Group B分别以不同的订阅关系订阅了同一个主题Topic A,这两个订阅关系互相独立,可以各自定义,不受影响。 不同消费者分组对于同一个主题的订阅相互独立
  • 同一个消费者分组对于不同主题的订阅也相互独立。如下图所示,消费者分组Group A订阅了两个主题Topic A和Topic B,对于Group A中的消费者来说,订阅的Topic A为一个订阅关系,订阅的Topic B为另外一个订阅关系,且这两个订阅关系互相独立,可以各自定义,不受影响。 同一个消费者分组对于不同主题的订阅也相互独立

消息过滤

rocketmq.apache.org/zh/docs/fea…

消费者订阅某个主题后,Apache RocketMQ 会将该主题中的所有消息投递给消费者。若消费者只需要关注部分消息,可通过设置过滤条件在 Apache RocketMQ 服务端进行过滤只获取到需要关注的消息子集,避免接收到大量无效的消息。本文介绍消息过滤的定义、原理、分类及不同过滤方式的使用方法、配置示例等。

应用场景

Apache RocketMQ 作为发布订阅模型的消息中间件广泛应用于上下游业务集成场景。在实际业务场景中,同一个主题下的消息往往会被多个不同的下游业务方处理,各下游的处理逻辑不同,只关注自身逻辑需要的消息子集。

使用 Apache RocketMQ 的消息过滤功能,可以帮助消费者更高效地过滤自己需要的消息集合,避免大量无效消息投递给消费者,降低下游系统处理压力。

Apache RocketMQ 主要解决的单个业务域同一个主题内不同消息子集的过滤问题,一般是基于同一业务下更具体的分类进行过滤匹配。如果是需要对不同业务域的消息进行拆分,建议使用不同主题处理不同业务域的消息

功能概述

消息过滤定义

过滤的含义指的是将符合条件的消息投递给消费者,而不是将匹配到的消息过滤掉

Apache RocketMQ 的消息过滤功能通过生产者和消费者对消息的属性、标签进行定义,并在 Apache RocketMQ 服务端根据过滤条件进行筛选匹配,将符合条件的消息投递给消费者进行消费。

消息过滤原理

消息过滤原理 消息过滤主要通过以下几个关键流程实现

  • 生产者:生产者在初始化消息时预先为消息设置一些属性和标签,用于后续消费时指定过滤目标

  • 消费者:消费者在初始化及后续消费流程中通过调用订阅关系注册接口,向服务端上报需要订阅指定主题的哪些消息,即过滤条件

  • 服务端:消费者获取消息时会触发服务端的动态过滤计算,Apache RocketMQ 服务端根据消费者上报的过滤条件的表达式进行匹配,并将符合条件的消息投递给消费者

消息过滤分类

Apache RocketMQ 支持Tag标签过滤SQL属性过滤,这两种过滤方式对比如下: 两种过滤方式对比 具体的使用方式及示例,请参见下文的Tag标签过滤和SQL属性过滤。

订阅关系一致性

过滤表达式属于订阅关系的一部分,Apache RocketMQ 的领域模型规定,同一消费者分组内的多个消费者的订阅关系包括过滤表达式,必须保持一致,否则可能会导致部分消息消费不到。更多信息,请参见订阅关系(Subscription)

Tag标签过滤

Tag标签过滤方式是 Apache RocketMQ 提供的基础消息过滤能力,基于生产者为消息设置的Tag标签进行匹配。生产者在发送消息时,设置消息的Tag标签,消费者需指定已有的Tag标签来进行匹配订阅。

场景示例

以下图电商交易场景为例,从客户下单收到商品这一过程会生产一系列消息:

  • 订单消息
  • 支付消息
  • 物流消息

这些消息会发送到名称为Trade_Topic的Topic中,被各个不同的下游系统所订阅:

  • 支付系统:只需订阅支付消息。
  • 物流系统:只需订阅物流消息。
  • 交易成功率分析系统:需订阅订单和支付消息。
  • 实时计算系统:需要订阅所有和交易相关的消息。

过滤效果如下图所示: 电商交易场景

Tag标签设置

  • Tag由生产者发送消息时设置每条消息允许设置一个Tag标签
  • Tag使用可见字符,建议长度不超过128字符。

Tag标签过滤规则

Tag标签过滤为精准字符串匹配,过滤规则设置格式如下:

  • 单Tag匹配:过滤表达式为目标Tag。表示只有消息标签为指定目标Tag的消息符合匹配条件,会被发送给消费者。
  • 多Tag匹配多个Tag之间为或的关系,不同Tag间使用两个竖线(||)隔开。例如,Tag1||Tag2||Tag3,表示标签为Tag1或Tag2或Tag3的消息都满足匹配条件,都会被发送给消费者进行消费。
  • 全部匹配使用星号(*)作为全匹配表达式。表示主题下的所有消息都将被发送给消费者进行消费。

SQL属性过滤

SQL属性过滤是 Apache RocketMQ 提供的高级消息过滤方式,通过生产者为消息设置的属性(Key)及属性值(Value)进行匹配。生产者在发送消息时可设置多个属性,消费者订阅时可设置SQL语法的过滤表达式过滤多个属性

Tag是一种系统属性,所以SQL过滤方式也兼容Tag标签过滤。在SQL语法中,Tag的属性名称TAGS