背景与场景
消息隔离的原则
与RPC隔离的原则一样,异曲同工之妙
- 灰度消费者,只能消费灰度消息(特定标签);
- 默认正常消费者,可以同时消费正常消息和灰度消息(自动识别),灰度消息可以在灰度和正常消费者之间穿梭;(兜底)
消息消费者的行为如下:
- 未打标的环境节点,默认会消费所有环境生产出来的消息。
- 打标的环境节点,只消费在相同标签环境生产出来的消息。
灰度场景
大部分场景下 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类型的消息灰度。