作者:亦盏

之前的系列文章中,咱们现现已过全链路金丝雀发布这个功用来介绍了 MSE 对于全链路流量操控的场景,咱们现已了解了 Spring Cloud 和 Dubbo 这一类 RPC 调用的全链路灰度应该如何完成,可是没有涉及到音讯这类异步场景下的流量操控,今日咱们将以上次介绍过的《如何用 20 分钟就能取得同款企业级全链路灰度才干?》中的场景为根底,来进一步介绍音讯场景的全链路灰度。

尽管绝大多数事务场景下对于音讯的灰度的要求并不像 RPC 的要求得这么严厉,可是在以下两个场景下,仍是会对音讯的全链路有一定的诉求的。

1、第一种场景是在音讯消费时,可能会发生新的 RPC 调用,假如没有在音讯这一环去遵从之前设定好的全链路流量操控的规矩,会导致经过音讯发生的这部分流量“逃逸”,然后导致全链路灰度的规矩遭到破坏,导致呈现不符合预期的状况。

为了避免呈现这个状况,咱们需求在消费时分将音讯里本来的流量标复原,并在 RPC 调用的时分遵从本来的规矩。咱们经过架构图来详细描绘一下,满足这个逻辑之后,调用链路是怎样的,从下图中咱们能够看到,灰度和基线环境出产出来的音讯,尽管在音讯推送的时分是随机的,可是在消费过程中,发生的新的 RPC 调用,仍是能够回到流量本来所属的环境。

在这儿刺进图片描绘

2、第二种场景需求更加严厉的音讯灰度隔离。比方当音讯的消费逻辑进行了修正时,这时分期望经过小流量的方法来验证新的音讯消费逻辑的正确性,要严厉地要求灰度的音讯只能被推送给灰度的音讯顾客。

在这儿刺进图片描绘

今日咱们就来实操一下第二种场景音讯的全链路灰度,现在 MSE 仅支撑 RocketMQ 音讯的灰度。若您运用的是开源版 RocketMQ,那么版别需求在 4.5.0 及以上,若您运用的是阿里云商业版 RocketMQ,那么需求运用铂金版,且 Ons Client 版别在 1.8.0.Final 及以上。假如只是想运用第一种场景,只需求给 B 运用敞开全链路灰度的功用即可,不需求做额定的音讯灰度相关的装备。

在这次最佳实践的操作中,咱们是将运用布置在阿里云容器服务 Kubernetes 版别,即 ACK 集群来演示,可是事实上,音讯灰度对于运用的布置形式是没有限制性要求的,您能够参考 MSE 协助文档,找到自己所运用的布置形式对应的接入方法,也能运用音讯全链路灰度。

前提条件

1、注册 MSE 专业版,请参见注册 MSE 微服务办理专业版[1]。

2、创立 ACK 集群,请参见创立 Kubernetes 集群[2]。

操作过程

过程一:接入 MSE 微服务办理

1、装置 mse-ack-pilot

a.登录容器服务操控台[3]。

b.在左侧导航栏单击商场 > 运用目录

c.在运用目录页面点击阿里云运用,挑选微服务,并单击 ack-mse-pilot

d.在 ack-mse-pilot 页面右侧集群列表中挑选集群,然后单击创立。

在这儿刺进图片描绘

装置 MSE 微服务办理组件大约需求 2 分钟,请耐性等候。

创立成功后,会自动跳转到目标集群的 Helm 页面,检查装置成果。假如呈现以下页面,展现相关资源,则说明装置成功。

在这儿刺进图片描绘

2、为 ACK 命名空间中的运用敞开 MSE 微服务办理

a.登录 MSE 办理中心操控台[4],假如您尚未注册 MSE 微服务办理,请依据提示注册。

b.在左侧导航栏挑选微服务办理中心 > Kubernetes 集群列表

c.在 Kubernetes 集群列表页面搜索框列表中挑选集群称号或集群 ID,然后输入相应的关键字,单击搜索图标。

d.单击目标集群操作列的办理

e.在集群概况页面命名空间列表区域,单击目标命名空间操作列下的敞开微服务办理

f.在敞开微服务办理对话框中单击确认。

在这儿刺进图片描绘

过程二:复原线上场景

首要,咱们将别离布置 spring-cloud-zuul、spring-cloud-a、spring-cloud-b、spring-cloud-c 这四个事务运用,以及注册中心 Nacos Server 和音讯服务 RocketMQ Server,模拟出一个实在的调用链路。

Demo 运用的结构图下图,运用之间的调用,既包含了 Spring Cloud 的调用,也包含了 Dubbo 的调用,掩盖了当前市面上最常用的两种微服务结构。其中 C 运用会出产出 RocketMQ 音讯,由 A 运用进行消费,A 在消费音讯时,也会建议新的调用。这些运用都是最简单的 Spring Cloud 、 Dubbo 和 RocketMQ 的标准用法,您也能够直接在 github.com/aliyun/alib… 项目上检查源码。

在这儿刺进图片描绘

布置之前,简单介绍一下这个调用链路

spring-cloud-zuul 运用在收到 “/A/dubbo” 的恳求时,会把恳求转发给 spring-cloud-a ,然后 spring-cloud-a 经过 dubbo 协议去拜访 spring-cloud-b, spring-cloud-b 也经过 dubbo 协议去拜访 spring-cloud-c,spring-cloud-c 在收到恳求后,会出产一个音讯,并返回自己的环境标签和 ip。这些出产出来的音讯会由 spring-cloud-a 运用消费,spring-cloud-a 运用在消费音讯的时分,会经过 spring cloud 去调用 B,B 从而经过 spring cloud 去调用 C,并且将成果输出到自己的日志中。

当咱们调用 /A/dubbo 的时分
返回值是这样 A[10.25.0.32] -> B[10.25.0.152] -> C[10.25.0.30]
一起,A 运用在接收到音讯之后,输出的日志如下
2021-12-28 10:58:50.301  INFO 1 --- [essageThread_15] c.a.mse.demo.service.MqConsumer          : topic:TEST_MQ,producer:C[10.25.0.30],invoke result:A[10.25.0.32] -> B[10.25.0.152] -> C[10.25.0.30]

熟悉了调用链路之后,咱们继续布置运用,您能够运用 kubectl 或许直接运用 ACK 操控台来布置运用。布置所运用的 yaml 文件如下,您相同能够直接在 github.com/aliyun/alib… 上获取对应的源码。

# 布置 Nacos Server
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nacos-server
spec:
  selector:
    matchLabels:
      app: nacos-server
  template:
    metadata:
      annotations:
      labels:
        app: nacos-server
    spec:
      containers:
        - env:
            - name: MODE
              value: "standalone"
          image: registry.cn-shanghai.aliyuncs.com/yizhan/nacos-server:latest
          imagePullPolicy: IfNotPresent
          name: nacos-server
          ports:
            - containerPort: 8848
---
apiVersion: v1
kind: Service
metadata:
  name: nacos-server
spec:
  type: ClusterIP
  selector:
    app: nacos-server
  ports:
    - name: http
      port: 8848
      targetPort: 8848
# 布置事务运用
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: spring-cloud-zuul
spec:
  selector:
    matchLabels:
      app: spring-cloud-zuul
  template:
    metadata:
      annotations:
        msePilotCreateAppName: spring-cloud-zuul
      labels:
        app: spring-cloud-zuul
    spec:
      containers:
        - env:
            - name: JAVA_HOME
              value: /usr/lib/jvm/java-1.8-openjdk/jre
            - name: enable.mq.invoke
              value: 'true'
          image: registry.cn-shanghai.aliyuncs.com/yizhan/spring-cloud-zuul:1.0.0
          imagePullPolicy: Always
          name: spring-cloud-zuul
          ports:
            - containerPort: 20000
---
apiVersion: v1
kind: Service
metadata:
  annotations:
    service.beta.kubernetes.io/alibaba-cloud-loadbalancer-spec: slb.s1.small
    service.beta.kubernetes.io/alicloud-loadbalancer-address-type: internet
  name: zuul-slb
spec:
  ports:
    - port: 80
      protocol: TCP
      targetPort: 20000
  selector:
    app: spring-cloud-zuul
  type: LoadBalancer
status:
  loadBalancer: {}
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: spring-cloud-a
spec:
  selector:
    matchLabels:
      app: spring-cloud-a
  template:
    metadata:
      annotations:
        msePilotCreateAppName: spring-cloud-a
      labels:
        app: spring-cloud-a
    spec:
      containers:
        - env:
            - name: JAVA_HOME
              value: /usr/lib/jvm/java-1.8-openjdk/jre
          image: registry.cn-shanghai.aliyuncs.com/yizhan/spring-cloud-a:1.0.0
          imagePullPolicy: Always
          name: spring-cloud-a
          ports:
            - containerPort: 20001
          livenessProbe:
            tcpSocket:
              port: 20001
            initialDelaySeconds: 10
            periodSeconds: 30
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: spring-cloud-b
spec:
  selector:
    matchLabels:
      app: spring-cloud-b
  template:
    metadata:
      annotations:
        msePilotCreateAppName: spring-cloud-b
      labels:
        app: spring-cloud-b
    spec:
      containers:
        - env:
            - name: JAVA_HOME
              value: /usr/lib/jvm/java-1.8-openjdk/jre
          image: registry.cn-shanghai.aliyuncs.com/yizhan/spring-cloud-b:1.0.0
          imagePullPolicy: Always
          name: spring-cloud-b
          ports:
            - containerPort: 20002
          livenessProbe:
            tcpSocket:
              port: 20002
            initialDelaySeconds: 10
            periodSeconds: 30
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: spring-cloud-c
spec:
  selector:
    matchLabels:
      app: spring-cloud-c
  template:
    metadata:
      annotations:
        msePilotCreateAppName: spring-cloud-c
      labels:
        app: spring-cloud-c
    spec:
      containers:
        - env:
            - name: JAVA_HOME
              value: /usr/lib/jvm/java-1.8-openjdk/jre
          image: registry.cn-shanghai.aliyuncs.com/yizhan/spring-cloud-c:1.0.0
          imagePullPolicy: Always
          name: spring-cloud-c
          ports:
            - containerPort: 20003
          livenessProbe:
            tcpSocket:
              port: 20003
            initialDelaySeconds: 10
            periodSeconds: 30
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: rockectmq-broker
spec:
  selector:
    matchLabels:
      app: rockectmq-broker
  template:
    metadata:
      labels:
        app: rockectmq-broker
    spec:
      containers:
        - command:
            - sh
            - mqbroker
            - '-n'
            - 'mqnamesrv:9876'
            - '-c /home/rocketmq/rocketmq-4.5.0/conf/broker.conf'
          env:
            - name: ROCKETMQ_HOME
              value: /home/rocketmq/rocketmq-4.5.0
          image: registry.cn-shanghai.aliyuncs.com/yizhan/rocketmq:4.5.0
          imagePullPolicy: Always
          name: rockectmq-broker
          ports:
            - containerPort: 9876
              protocol: TCP
            - containerPort: 10911
              protocol: TCP
            - containerPort: 10912
              protocol: TCP
            - containerPort: 10909
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: rocketmq-name-server
spec:
  selector:
    matchLabels:
      app: rocketmq-name-server
  template:
    metadata:
      labels:
        app: rocketmq-name-server
    spec:
      containers:
        - command:
            - sh
            - mqnamesrv
          env:
            - name: ROCKETMQ_HOME
              value: /home/rocketmq/rocketmq-4.5.0
          image: registry.cn-shanghai.aliyuncs.com/yizhan/rocketmq:4.5.0
          imagePullPolicy: Always
          name: rocketmq-name-server
          ports:
            - containerPort: 9876
              protocol: TCP
            - containerPort: 10911
              protocol: TCP
            - containerPort: 10912
              protocol: TCP
            - containerPort: 10909
              protocol: TCP
---  
apiVersion: v1
kind: Service
metadata:
  name: mqnamesrv
spec:
  type: ClusterIP
  selector:
    app: rocketmq-name-server
  ports:
    - name: mqnamesrv-9876-9876
      port: 9876
      targetPort: 9876

装置成功后,示例如下:

➜  ~ kubectl get svc,deploy
NAME                   TYPE           CLUSTER-IP        EXTERNAL-IP    PORT(S)        AGE
service/kubernetes     ClusterIP      192.168.0.1       <none>         443/TCP        7d
service/mqnamesrv      ClusterIP      192.168.213.38    <none>         9876/TCP       47h
service/nacos-server   ClusterIP      192.168.24.189    <none>         8848/TCP       47h
service/zuul-slb       LoadBalancer   192.168.189.111   123.56.253.4   80:30260/TCP   47h
NAME                                   READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/nacos-server           1/1     1            1           4m
deployment.apps/rockectmq-broker       1/1     1            1           4m
deployment.apps/rocketmq-name-server   1/1     1            1           5m
deployment.apps/spring-cloud-a         1/1     1            1           5m
deployment.apps/spring-cloud-b         1/1     1            1           5m
deployment.apps/spring-cloud-c         1/1     1            1           5m
deployment.apps/spring-cloud-zuul      1/1     1            1           5m

一起这儿咱们能够经过 zuul-slb 来验证一下方才所说的调用链路

➜  ~ curl http://123.56.253.4/A/dubbo
A[10.25.0.32] -> B[10.25.0.152] -> C[10.25.0.30]

过程三:敞开音讯灰度功用

现在依据操控台的提示,在音讯的出产者 spring-cloud-c 和音讯的顾客 spring-cloud-a 都敞开音讯的灰度。咱们直接经过 MSE 的操控台敞开,点击进入运用的概况页,挑选“音讯灰度”标签。

在这儿刺进图片描绘

能够看到,在未打标环境疏忽的标签中,咱们输入了 gray,这儿意味着,带着 gray 环境标的音讯,只能由 spring-cloud-a-gray 消费,不能由 spring-cloud-a 来消费。

1、这儿需求额定说明一下,由于考虑到实践场景中,spring-cloud-c 运用和 spring-cloud-a 运用的一切者可能不是同一个人,纷歧定能够做到两者一起进行灰度发布同步的操作,所以在音讯的灰度中,未打标环境默许的行为是消费一切音讯。这样 spring-cloud-c 在进行灰度发布的时分,能够不需求强制 spring-cloud-a 运用也一定要一起灰度发布。
2、咱们把未打标环境消费行为的挑选权交给 spring-cloud-a 的一切者,假如需求完成未打标环境不消费 c-gray 出产出来的音讯,只需求在操控台进行装备即可,装备之后实时收效。

  • 运用此功用您无需修正运用的代码和装备。

  • 音讯的出产者和音讯的顾客,需求一起敞开音讯灰度,音讯的灰度功用才干收效。

  • 音讯类型现在只支撑 RocketMQ,包含开源版别和阿里云商业版。

    • 假如您运用开源 RocketMQ,则 RocketMQ Server 和 RocketMQ Client 都需求运用 4.5.0 及以上版别。

    • 假如您运用阿里云 RocketMQ,需求运用铂金版,且 Ons Client 运用 1.8.0.Final 及以上版别。

  • 敞开音讯灰度后,MSE 会修正音讯的 Consumer Group。例如本来的 Consumer Group 为 group1,环境标签为 gray,敞开音讯灰度后,则 group 会被修正成 group1_gray,假如您运用的是阿里云 RocketMQ ,请提早创立好 group。

  • 默许运用 SQL92 的过滤方法,假如您运用的开源 RocketMQ,需求在服务端敞开此功用(即在 broker.conf 中装备 enablePropertyFilter=true)。

  • 默许状况下,未打标节点将消费一切环境的音讯,若需求指定 未打标环节点 不消费 某个标签环境出产出来的音讯,请装备“未打标环境疏忽的标签”,修正此装备后动态收效,无需重启运用。

过程四:重启节点,布置新版别运用,并引进流量进行验证

首要,由于敞开和封闭运用的音讯灰度功用后都需求重启节点才干收效,所以首要咱们需求重启一下 spring-cloud-a 和 spring-cloud-c 运用,重启的方法能够在操控台上挑选重新布置,或许直接运用 kubectl 指令删去现有的 pod。

在这儿刺进图片描绘

然后,继续运用 yaml 文件的方法在 Kubernetes 集群中布置新版别的 spring-cloud-a-gray、spring-cloud-b-gray 和 spring-cloud-c-gray

apiVersion: apps/v1
kind: Deployment
metadata:
  name: spring-cloud-a-gray
spec:
  selector:
    matchLabels:
      app: spring-cloud-a-gray
  template:
    metadata:
      annotations:
        alicloud.service.tag: gray
        msePilotCreateAppName: spring-cloud-a
      labels:
        app: spring-cloud-a-gray
    spec:
      containers:
        - env:
            - name: JAVA_HOME
              value: /usr/lib/jvm/java-1.8-openjdk/jre
          image: registry.cn-shanghai.aliyuncs.com/yizhan/spring-cloud-a:1.0.0
          imagePullPolicy: Always
          name: spring-cloud-a-gray
          ports:
            - containerPort: 20001
          livenessProbe:
            tcpSocket:
              port: 20001
            initialDelaySeconds: 10
            periodSeconds: 30
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: spring-cloud-b-gray
spec:
  selector:
    matchLabels:
      app: spring-cloud-b-gray
  template:
    metadata:
      annotations:
        alicloud.service.tag: gray
        msePilotCreateAppName: spring-cloud-b
      labels:
        app: spring-cloud-b-gray
    spec:
      containers:
        - env:
            - name: JAVA_HOME
              value: /usr/lib/jvm/java-1.8-openjdk/jre
          image: registry.cn-shanghai.aliyuncs.com/yizhan/spring-cloud-b:1.0.0
          imagePullPolicy: Always
          name: spring-cloud-b-gray
          ports:
            - containerPort: 20002
          livenessProbe:
            tcpSocket:
              port: 20002
            initialDelaySeconds: 10
            periodSeconds: 30
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: spring-cloud-c-gray
spec:
  selector:
    matchLabels:
      app: spring-cloud-c-gray
  template:
    metadata:
      annotations:
        alicloud.service.tag: gray
        msePilotCreateAppName: spring-cloud-c
      labels:
        app: spring-cloud-c-gray
    spec:
      containers:
        - env:
            - name: JAVA_HOME
              value: /usr/lib/jvm/java-1.8-openjdk/jre
          image: registry.cn-shanghai.aliyuncs.com/yizhan/spring-cloud-c:1.0.0
          imagePullPolicy: Always
          name: spring-cloud-c-gray
          ports:
            - containerPort: 20003
          livenessProbe:
            tcpSocket:
              port: 20003
            initialDelaySeconds: 10
            periodSeconds: 30

1、布置完成之后,咱们引进流量,并进行验证

2、登录 MSE 办理中心操控台[4],挑选运用列表。

单击运用 spring-cloud-a 运用概况菜单,此刻能够看到,一切的流量恳求都是去往 spring-cloud-a 运用未打标的版别,即稳定版别。

在这儿刺进图片描绘

点击页面下方的 标签路由中的添加按钮,为 spring-cloud-a 运用的 gray 版别设置灰度规矩。

在这儿刺进图片描绘

在这儿刺进图片描绘

建议流量调用,咱们经过 zuul-slb,别离建议流量调用,并检查灰度的状况。

在这儿刺进图片描绘

在这儿刺进图片描绘

在这儿刺进图片描绘

咱们经过 spring-cloud-a 和 spring-cloud-a-gray 的日志去检查音讯消费的状况。能够看到,音讯的灰度功用现已收效, spring-cloud-a-gray 这个环境,只会消费带有 gray 标的音讯,spring-cloud-a 这个环境,只会消费未打标的流量出产出来的音讯。

在截图中咱们能够看见,spring-cloud-a-gray 环境输出的日志 topic:TEST_MQ, producer: Cgray [10.25.0.102] , invoke result: Agray[10.25.0.101] -> Bgray[10.25.0.25] -> Cgray[10.25.0.102], spring-cloud-a-gray 只会消费 Cgray 出产出来的音讯,并且消费音讯过程中建议的 Spring Cloud 调用,成果也是 Agray[10.25.0.101] -> Bgray[10.25.0.25] -> Cgray[10.25.0.102],即在灰度环境闭环。

而 spring-cloud-a 这个环境,输出的日志为 topic:TEST_MQ,producer:C[10.25.0.157],invoke result:A[10.25.0.100] -> B[10.25.0.152] -> C[10.25.0.157],只会消费 C 的基线环境出产出来的音讯,且在这个过程中建议的 Spring Cloud 调用,也是在基线环境闭环。

在这儿刺进图片描绘

在这儿刺进图片描绘

过程五:调整音讯的标签过滤规矩,并进行验证

由于考虑到实践场景中,spring-cloud-c 运用和 spring-cloud-a 运用的一切者可能不是同一个人,纷歧定能够做到两者一起进行灰度发布同步的操作,所以在音讯的灰度中,未打标环境默许的行为是消费一切音讯。这样 spring-cloud-c 在进行灰度发布的时分,能够不需求强制 spring-cloud-a 运用也一定要一起灰度发布,且运用相同的环境标。

spring-cloud-a 在消费时分,未打标环境的行为的挑选权是交给 spring-cloud-a 的一切者,假如需求完成未打标环境不消费 c-gray 出产出来的音讯,只需求在操控台进行装备即可,装备之后实时收效。

1、调整 spring-cloud-a 未打标环境的过滤规矩。比方这儿咱们要挑选未打标环境不再消费 gray 环境出产出来的音讯,只需求在“未打标环境疏忽的标签”里边挑选 gray,然后点击确定即可。

在这儿刺进图片描绘

2、调整规矩之后,规矩是能够动态地收效,不需求进行重启的操作,咱们直接检查 spring-cloud-a 的日志,验证规矩调整收效。

从这个日志中,咱们能够看到,此刻基线环境能够一起消费 gray 和 基线环境出产出来的音讯,并且在消费对应环境音讯时发生的 Spring Cloud 调用别离路由到 gray 和 基线环境中。

在这儿刺进图片描绘

操作总结

1、全链路音讯灰度的整个过程是不需求修正任何代码和装备的。

2、现在仅支撑 RocketMQ,Client 版别需求在 4.5.0 之后的版别。RocketMQ Server 端需求支撑 SQL92 规矩过滤,即开源 RocketMQ 需求装备 enablePropertyFilter=true,阿里云 RocketMQ 需求运用铂金版。

3、敞开音讯灰度后,MSE Agent 会修正音讯顾客的 group,如本来的消费 group 为 group1,环境标签为 gray,则 group 会被修正成 group1_gray,假如运用的是阿里云 RocketMQ,需求提早创立好修正后的 group。

4、敞开和封闭音讯灰度后,运用需求重启才干收效;修正未打标环境疏忽的标签功用能够动态收效,不需求重启。

相关链接

[1] MSE 微服务办理专业版:

help.aliyun.com/document_de…

[2] Kubernetes 集群:

help.aliyun.com/document_de…

[3] 容器服务操控台:
cs.console.aliyun.com/

[4] MSE 办理中心操控台
mse.console.aliyun.com/#/msc/home

点击此处,前往 MSE 官网检查更多概况!

发布云原生技能最新资讯、聚集云原生技能最全内容,定期举行云原生活动、直播,阿里产品及用户最佳实践发布。与你并肩探索云原生技能点滴,共享你需求的云原生内容。

重视【阿里巴巴云原生】大众号,获取更多云原生实时资讯!