消息中间件如何实现蓝绿发布

消息中间件在分布式架构体系中的应用非常广泛,要想实现蓝绿发布,只在微服务调用层面实现还远远不够。在进行具体的方案设计之前,我们还是先来看一下我们这个项目中消息中间件的部署情况:

image.png

这里有四个应用,简单解释一下。

  1. 应用 1 支持蓝绿发布,并且处理完业务后,需要向消息中间件中的 topic_A 主题发送消息。
  2. 应用 2 不支持蓝绿发布,但同样需要在处理完业务后,向消息中间件中的 topic_A 发送消息。
  3. 应用 3 不支持蓝绿发布,需要处理完业务逻辑后,向消息中间件中的主题 topic-B 发送消息。
  4. 应用 4 中创建了两个消费组,其中 consumer_group_a 订阅 topicA,支持接入蓝绿;而 consumer_group_b 没有接入蓝绿。

那么怎么基于这一条件来设计和实施蓝绿方案呢?这又涉及到一个隔离机制的问题。因为无论是蓝绿发布还是全链路压测,需要着重解决的一个问题就是消息的隔离性。蓝绿发布的本质就是对消息进行分类,蓝颜色的消息只能被蓝颜色的消费者消费,绿颜色的消息只能被绿颜色的消费者消费。

消息中间件领域通常有“基于消息主题”和“基于消息属性”两种隔离机制。我们先来看第一种隔离机制,基于消息主题的物理隔离机制:


image.png

基于主题的隔离机制在消息服务端是分开存储的,属于物理层面的隔离。在消息消费端,由于应用使用不同的消费组进行消费,每一个消费组在物理层面也是互不影响的,每一个消费组有独立的线程池、消费进度等。

消息中间件中的另外一种隔离机制是基于消息属性的。例如,蓝绿两种颜色的消息使用的是同一个主题,但我们可以在消息中添加一个属性,标识这条消息的颜色。其存储示意图如下:

image.png

这样,不同属性的消息就可以共用一个主题了。消息发送端在发送消息时,会为消息设置相应的属性,将它存储到消息的属性中。然后单个消费端应用会创建蓝绿两个消费组,都订阅同一个主题。消费组拉取到消息后,需要先解码找到对应的消息属性,蓝颜色消费者只真正处理属性为 BLUE 的消息,那些属性为 GREEN 的消息会默认向服务端返回“消费成功”。这样就在客户端实现了消息过滤机制。

目前主流消息中间件的隔离机制都是基于消息属性的。

基于消息属性的蓝绿设计方案

基于消息属性的隔离机制的一个显著的特点是,蓝绿消息使用的是同一个主题。因此我们需要在不同环境的生产者发送消息时,为消息设置不同的颜色。

和在微服务领域实现蓝绿发布一样,我们通过系统参数为应用设置所属环境:


image.png

通常每一家公司都会有一个统一的开发框架,会基于目前主流的 RocketMQ、Kafka 客户端进行封装,或者使用类似 rocketmq-spring 这样的开源类库。为了防止对业务代码进行侵入,通常会采用拦截器机制,拦截消息发送 API,然后在拦截器中根据系统参数,为消息设置对应的属性。从系统参数中获取颜色值的示例代码如下:

private static final String COLOR_SYS_PROP = "color";
private static final String COLOR_ENV = System.getProperty(COLOR_SYS_PROP, "");

当不同环境的消息发送者将消息发送到消息服务器后,消费端就要按颜色将消费分开了。虽然消费端的隔离机制是通过不同的消费组来实现的,每一个消费组拥有自己独立的消费者线程池、消费进度,组与组之间互不影响。但是消费端不能简单粗暴地用系统参数来区分消费组的颜色,因为一个应用中可能存在多个消费组,这些消费组并不都开启了蓝绿机制。

所以基于消费组的蓝绿定义,首先需要在消费者的元信息中定义。例如,我们公司在申请消费组时,可以根据环境为消费组设置是否启用蓝绿机制。如下图所示:


image.png

蓝绿发布状态可选择:蓝、绿、所有。这里的“所有”表示消费组未开启蓝绿,选择“蓝”或“绿”都表示消费组开启蓝绿。

消费组是如何进行消息过滤的呢?我们来看下部署示意图:

image.png

我们看应用 3 会部署在蓝、绿两个环境,但是在原始的镜头项目代码中我们只会定义一个基本的消费组,例如 dw_test_consumer_group,蓝绿发布要求我们这套代码用不同的系统属性定义后,就能分别实现消息的过滤。

那我们如何动态开启蓝绿发布机制呢?我总结了下面两个实现要点。

  • 应用启动时,首先获取系统参数 color 的值(如果有设置),并根据设置的值改写原消费组的名称。如果 color 的值为 BLUE,那我们在调用 RocketMQ 底层 DefaultMqPushConsumer 时,传入的消费组名称为 _BLUE_dw_test_consumer_group;如果 color 的值为 GREEN,那最终会创建的消费组名称就是 _GREEN_dw_test_consumer_group。

  • 消费者启动后开始处理消费,在真正调用用户定义的消息业务处理器(MessageListener)之前,需要将消息进行解码,然后提取消息属性中 color 的值,用 mqProColor 表示,如果 mqProColor 的值与系统参数 color 中的值相等,就调用用户定义的消息业务处理器。否则就认为消费成功,直接给 MQ 服务器返回“成功”,相当于跳过这条消息的处理。

这么乍一看,蓝颜色的消费者消费 color=BLUE 的消息,绿颜色的消费者消费 color=GREEN 的消息,这不是很“完美”地解决了蓝绿发布的问题了吗?

事实不是这样的。因为 topic 中发送的消息有可能不带颜色,例如应用 -1 需要发送消息到 TOPIC_A 中, 这个应用接入了蓝绿,会发送蓝色或者绿颜色的消息。但应用 -2 没有接入蓝绿,所以应用 -2 发送的消息是不包含颜色的。按照上面的方案,这部分消息将无法被消费,最终结果就是:消息丢失。

那怎么解决消息消费丢失的问题呢?

我们可以在消费组元信息中定义不带颜色的消息由哪个环境来消费。我在公司实践时,消费者的蓝绿发布状态有下面三个值。

  • 所有: 表示该消费组未接入蓝绿。
  • 蓝:表示该消费组接入蓝绿,并且消息属性中未带颜色的消息由蓝环境的消费者进行消费。
  • 绿:表示该消费组接入蓝绿,并且消息属性中未带颜色的消息由绿环境的消费者进行消费。

这样定义了之后,应用启动时,如果消费者的蓝绿状态为蓝,我们会同时启动两个消费组,一个消费组为 _BLUE_dw_test_consumer_group,用来专门消费蓝颜色的消费者;另外一个消费组为 dw_test_consumer_group,用来消费不带颜色的消息。蓝环境的应用在启动时只会创建一个消费组,那就是 _GREEN_dw_test_consumer_group。

同时,我们还支持在蓝绿之间进行切换。如果将消费组的蓝绿状态由 BLUE 变为 GREEN,我们会将原本在蓝环境的 dw_test_consumer_group 关闭,然后在绿环境中新增一个 dw_test_consumer_group 消费组。这样,我们就在消息中间件层面实现了蓝绿发布。

实现蓝绿的关键其实最终都落在了“如何有效隔离消息”这个问题上。基于消息属性的隔离,是在发送端使用一个主题,在每一条消息中添加一个属性 color 来存储消息的颜色,而消费端采取不同的消费组来消费消息。其中,蓝颜色的消息由蓝消费组消费,绿颜色的消息由绿消费组消费,没有颜色的消息由默认消费组来消费。这本质上是在消费端将数据从服务端全量拉取下来,然后在消费端进行了一层过滤,各个消费组都会读取到很多无效数据,无形中放大了拉取消息的调用次数。

而基于主题的隔离机制,是在消息发送时就将消息分别发送到不同的主题中,在消费端对各个消费组进行分工。蓝颜色的消费组只订阅蓝颜色主题,绿颜色的消费者只订阅绿颜色的主题,这就实现了有针对性的消费,效率更高。

此文章为5月Day25学习笔记,内容来源于极客时间《中间件核心技术与实战》,强烈推荐该课程

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,701评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,649评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,037评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,994评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,018评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,796评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,481评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,370评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,868评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,014评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,153评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,832评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,494评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,039评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,156评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,437评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,131评论 2 356

推荐阅读更多精彩内容