概述
本文介紹在使用消息隊列(RocketMQ)這種異步場景下,可以在不修改業務代碼的情況下,實現異步場景的灰度,從而實現全鏈路灰度。本文介紹基于消息隊列RocketMQ實現全鏈路灰度。
背景介紹
在大多數業務場景中對于消息的灰度并沒有RPC調用那么嚴格,但是當全鏈路灰度調用中涉及到消息消費時,如果消息消費沒有按照全鏈路流量規則路由,則會導致通過消息產生的流量逃逸,從而破壞全鏈路規則,導致出現一些不符合預期的情況。
如下圖所示,本文分別部署網關、app-a、app-a-gray、app-b、app-b-gray、app-c、app-c-gray以及RocketMQ,模擬一個真實的全鏈路灰度場景。
通過網關調用app-a應用的接口,當滿足路由規則后,灰度流量會被路由到app-a-gray,app-a-gray又會調用app-b-gray,隨后由app-b-gray發送灰度消息,app-c-gray將會收到灰度消息,而app-c不會收到灰度消息。

前提條件
- 用戶已開通微服務治理中心企業版。
- 用戶已開通云容器引擎。
- 用戶已部署RocketMQ,且RocketMQ版本在4.5.0以上,broker.conf中已配置enablePropertyFilter=true。
部署Demo應用
準備自建入口網關msgc-zuul,準備應用msgc-app-a,msgc-app-b和msgc-app-c。調用過程是msgc-app-a –> msgc-app-b -> msgc-app-c。
步驟1:在云容器引擎中安裝微服務治理插件:
- 登錄“云容器引擎”控制臺。
- 在左側菜單欄選擇“集群”,點擊目標集群。
- 在集群管理頁面點擊“插件”-“插件市場”,選擇“cubems”插件安裝。

步驟2:為應用開啟微服務治理能力:
- 登錄“云容器引擎”控制臺。
- 左側菜單欄選擇“集群”,點擊目標集群。
- 在集群管理頁面點擊“工作負載”-“無狀態”,選擇目標命名空間。

- 在Deployment列表頁選擇指定Deployment,并點擊“全量替換”,進入Deployment編輯頁。
- 在Deployment編輯頁點擊“顯示高級設置”,新增“Pod標簽”: mseCubeMsAutoEnable:on。

- 在發布應用時,配置指定環境變量,可指定注入微服務治理中心的應用名、命名空間和標簽等信息。
環境變量配置如下:
| 環境變量名 | 環境變量值 |
|---|---|
| MSE_APP_NAME | 接入到微服務治理中心的應用名。 |
| MSE_SERVICE_TAG | 應用標簽信息,如灰度應用可配置gray。 |
| MSE_NAMESPACE(選填) | 接入到微服務治理中心的命名空間,默認為:default。 |
- 完成編輯后點擊“提交”,重新發布容器即可接入。
app-a應用的配置
基線:
apiVersion: "apps/v1"
kind: "Deployment"
metadata:
name: "app-a"
namespace: "default"
spec:
progressDeadlineSeconds: 600
replicas: 1
revisionHistoryLimit: 10
selector:
matchLabels:
name: "app-a"
template:
metadata:
labels:
mseCubeMsAutoEnable: "on"
name: "app-a"
spec:
containers:
- env:
- name: "MSE_APP_NAME"
value: "app-a"
image: "鏡像倉庫域名/xxx/app-a:latest"
imagePullPolicy: "Always"
name: "app-a"
ports:
- containerPort: 26160
livenessProbe:
tcpSocket:
port: 26160
initialDelaySeconds: 10
periodSeconds: 30
resources:
limits:
cpu: "1"
memory: "1Gi"
requests:
cpu: "1"
memory: "1Gi"
灰度:
apiVersion: "apps/v1"
kind: "Deployment"
metadata:
name: "app-a"
namespace: "default"
spec:
progressDeadlineSeconds: 600
replicas: 1
revisionHistoryLimit: 10
selector:
matchLabels:
name: "app-a"
template:
metadata:
labels:
mseCubeMsAutoEnable: "on"
name: "app-a"
spec:
containers:
- env:
- name: "MSE_APP_NAME"
value: "app-a"
- name: "MSE_SERVICE_TAG"
value: "gray"
image: "鏡像倉庫域名/xxx/app-a:latest"
imagePullPolicy: "Always"
name: "app-a"
ports:
- containerPort: 26160
livenessProbe:
tcpSocket:
port: 26160
initialDelaySeconds: 10
periodSeconds: 30
resources:
limits:
cpu: "1"
memory: "1Gi"
requests:
cpu: "1"
memory: "1Gi"
app-b應用的配置
基線:
apiVersion: "apps/v1"
kind: "Deployment"
metadata:
name: "app-b"
namespace: "default"
spec:
progressDeadlineSeconds: 600
replicas: 1
revisionHistoryLimit: 10
selector:
matchLabels:
name: "app-b"
template:
metadata:
labels:
mseCubeMsAutoEnable: "on"
name: "app-b"
spec:
containers:
- env:
- name: "MSE_APP_NAME"
value: "app-b"
image: "鏡像倉庫域名/xxx/app-b:latest"
imagePullPolicy: "Always"
name: "app-b"
ports:
- containerPort: 26160
livenessProbe:
tcpSocket:
port: 26160
initialDelaySeconds: 10
periodSeconds: 30
resources:
limits:
cpu: "1"
memory: "1Gi"
requests:
cpu: "1"
memory: "1Gi"
灰度:
apiVersion: "apps/v1"
kind: "Deployment"
metadata:
name: "app-b"
namespace: "default"
spec:
progressDeadlineSeconds: 600
replicas: 1
revisionHistoryLimit: 10
selector:
matchLabels:
name: "app-b"
template:
metadata:
labels:
mseCubeMsAutoEnable: "on"
name: "app-b"
spec:
containers:
- env:
- name: "MSE_APP_NAME"
value: "app-b"
- name: "MSE_SERVICE_TAG"
value: "gray"
image: "鏡像倉庫域名/xxx/app-b:latest"
imagePullPolicy: "Always"
name: "app-b"
ports:
- containerPort: 26160
livenessProbe:
tcpSocket:
port: 26160
initialDelaySeconds: 10
periodSeconds: 30
resources:
limits:
cpu: "1"
memory: "1Gi"
requests:
cpu: "1"
memory: "1Gi"
app-c應用的配置
基線:
apiVersion: "apps/v1"
kind: "Deployment"
metadata:
name: "app-c"
namespace: "default"
spec:
progressDeadlineSeconds: 600
replicas: 1
revisionHistoryLimit: 10
selector:
matchLabels:
name: "app-c"
template:
metadata:
labels:
mseCubeMsAutoEnable: "on"
name: "app-c"
spec:
containers:
- env:
- name: "MSE_APP_NAME"
value: "app-c"
image: "鏡像倉庫域名/xxx/app-c:latest"
imagePullPolicy: "Always"
name: "app-c"
ports:
- containerPort: 26160
livenessProbe:
tcpSocket:
port: 26160
initialDelaySeconds: 10
periodSeconds: 30
resources:
limits:
cpu: "1"
memory: "1Gi"
requests:
cpu: "1"
memory: "1Gi"
灰度:
apiVersion: "apps/v1"
kind: "Deployment"
metadata:
name: "app-c"
namespace: "default"
spec:
progressDeadlineSeconds: 600
replicas: 1
revisionHistoryLimit: 10
selector:
matchLabels:
name: "app-c"
template:
metadata:
labels:
mseCubeMsAutoEnable: "on"
name: "app-c"
spec:
containers:
- env:
- name: "MSE_APP_NAME"
value: "app-c"
- name: "MSE_SERVICE_TAG"
value: "gray"
image: "鏡像倉庫域名/xxx/app-c:latest"
imagePullPolicy: "Always"
name: "app-c"
ports:
- containerPort: 26160
livenessProbe:
tcpSocket:
port: 26160
initialDelaySeconds: 10
periodSeconds: 30
resources:
limits:
cpu: "1"
memory: "1Gi"
requests:
cpu: "1"
memory: "1Gi"
zuul應用的配置:
apiVersion: "apps/v1"
kind: "Deployment"
metadata:
name: "zuul"
namespace: "default"
spec:
progressDeadlineSeconds: 600
replicas: 1
revisionHistoryLimit: 10
selector:
matchLabels:
name: "zuul"
template:
metadata:
labels:
mseCubeMsAutoEnable: "on"
name: "zuul"
spec:
containers:
- env:
- name: "MSE_APP_NAME"
value: "zuul"
image: "鏡像倉庫域名/xxx/zuul:latest"
imagePullPolicy: "Always"
name: "zuul"
ports:
- containerPort: 26160
livenessProbe:
tcpSocket:
port: 26160
initialDelaySeconds: 10
periodSeconds: 30
resources:
limits:
cpu: "1"
memory: "1Gi"
requests:
cpu: "1"
memory: "1Gi"
創建泳道組
- 登錄微服務治理控制臺。
- 在左側導航欄選擇全鏈路灰度,點擊創建泳道組。
- 在創建泳道組也頁面,設置泳道組相關參數,然后單擊確定。

創建泳道
- 找到目標泳道組頁面,點擊創建第一個分流泳道。
- 設置泳道名稱,選擇目標應用所屬標簽,創建泳道。

設置泳道組路由規則
設置路由規則,通過前端訪問傳過來不同的Header,在自建網關通過header判斷將流量路由到指定的泳道。

開啟消息灰度
- 進入微服務治理中心控制臺,點擊應用治理。
- 依次點擊應用卡片app-a、app-b和app-c。
- 選擇流量治理->消息灰度。
- 點擊編輯,分別打開app-a、app-b和app-c的消息灰度開關。

結果驗證
通過自建網關訪問app-a->app-b->app-c。
不攜帶Header參數tag=gray請求app-a接口/a/mqGray,發現app-c獲得消息消費。
攜帶Header參數tag=gray請求app-a接口/a/mqGray,發現app-c-gray獲得消息消費。