Skip to content

服务路由之元数据路由

lepdou edited this page May 29, 2022 · 10 revisions

元数据路由简介

元数据

元数据定义

服务实例通常带有一系列的标签信息,例如实例所属的机房信息、地域信息、环境信息等,这些信息统称为服务实例的元信息。实例注册到注册中心时,会带上自身的元数据信息。当消费方从注册中心获取到实例时,既可以同时获取到每个实例的元信息。

image

元数据来源

来源一:项目里的配置文件

在 Spring Cloud 项目里的 application.yml 中配置以下内容:

spring:
  cloud:
    tencent:
      metadata:
        content:
          idc: shanghai
          env: dev1

应用在启动注册时,会自动读取配置文件并带上 idc=shanghaienv=dev1 两个元数据信息。

通过配置文件的方式为最直接的方式,但是这种方式有一个最大的弊端,就是设置不同元数据值时需要重新编译打包。本质上的原因就是:大部分的元数据信息属于部署环境的信息或者叫机器的信息,而不是应用静态代码的信息。

来源二:应用启动参数

众所周知,Spring Boot 应用配置文件定义的配置项都可以通过 -D 启动参数覆盖,例如通过以下方式覆盖 env 值为 dev2 :

Java -jar -Dspring.cloud.tencent.metadata.content.env=dev2  demo.jar

通过启动参数的方式,可以在启动时动态修改元数据信息,而不用重新打包,从而解决了方式一的弊端。

但是这种方式也有弊端,就是谁负责修改启动脚本,加上 -D 参数呢?

大部分情况下,Java -jar 启动命令都放在项目里的 start.sh 脚本里,那也就是说,每次需要增加 -D 参数都要修改 start.sh,这本身也会增加维护成本。

来源三:环境变量

环境变量的方式则完全跟运行的应用解耦,是完完全全的机器信息。Spring Cloud Tencent 约定了前缀 SCT_METADATA_CONTENT_ 的环境变量为应用的元数据信息。例如:

SCT_METADATA_CONTENT_IDC=shanghai
SCT_METADATA_CONTENT_ENV=dev1

应用在启动时,则会自动携带 IDC=shanghaiENV=dev 的元数据。

通过环境变量的方式,paas 平台在拉起一台虚拟机或者一个容器 Pod 时,只需要设置相应的环境变量即可。而应用自身无需做任何其它事情。

以上几种方式,可以通过查看启动日志确认是否正确打标:

grep "Loaded static metadata info" xx.log

来源四:自定义实现 SPI (待实现)

前面三种方式为 SCT(Spring Cloud Tencent 的缩写) 内置的方式,但是并不一定符合每个公司自己的规范或者实现。例如:

  • 把元数据放到机器上的某一个配置文件里,例如 /etc/metadata
  • 启动时,调用 CMDB 的接口获取元信息

所以 SCT 定义了一个 SPI,方便用户自己实现元数据来源。SCT 在注册前,回调 SPI 获取元数据信息,并注册到注册中心。

元数据路由

上一小节介绍了什么是元数据以及元数据的来源,这一小节来介绍一下元数据路由。

既然服务实例都有元数据标签,在某些场景下,服务调用时优先调用到跟主调方具有相同元数据标签的被调方实例。如下图所示:

image

OrderSevice 调用 UserService 时,期望同环境实例优先互相调用。

最典型的实际场景为线下多特性环境:

image

当 SvcA 和 SvcD 需要在某一次迭代中进行联调时,无需部署一套包含全链路 SvcA,SvcB,SvcC,SvcD 的环境,一方面成本太高,另一方面 SvcB 和 SvcC 可能属于其它团队,不方便部署。 只需要在 dev1 里部署 SvcA 和 SvcD 打上 SCT_METADATA_CONTENT_ENV=dev1SCT_METADATA_CONTENT_TRANSITIVE=ENV 即可实现如上效果。

这里需要解释一下 SCT_METADATA_CONTENT_TRANSITIVE=ENV 环境变量的作用。

简单来讲:SCT_METADATA_CONTENT_TRANSITIVE 指定哪些元数据需要在调用链路上传递到下游服务。

在上面的场景下,每一个服务节点在调用下游服务时都要优先调用 ENV=dev1 的服务,而 ENV=dev1 这个信息是链路起始节点 SvcA 发出的请求里携带的。所以需要通过一种机制告诉 SCT 哪些元信息需要在链路上传递。

元数据路由操作篇

使用元数据路由非常简单,在第一章中其实都已经有介绍到,这一章节再总结一下。

步骤一:引入依赖

注意:1.5.0 版本后才支持元数据路由

<!-- 引入 SCT Bom -->
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>com.tencent.cloud</groupId>
            <artifactId>spring-cloud-tencent-dependencies</artifactId>
            <version>1.5.0-Hoxton.SR9</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<!-- 引入服务发现组件 -->
<dependency>
    <groupId>com.tencent.cloud</groupId>
    <artifactId>spring-cloud-starter-tencent-polaris-discovery</artifactId>
</dependency>

<!-- 引入服务路由组件 -->
<dependency>
    <groupId>com.tencent.cloud</groupId>
    <artifactId>spring-cloud-starter-tencent-polaris-router</artifactId>
</dependency>

元数据打标

方式一

spring:
  cloud:
    tencent:
      metadata:
        content:
          idc: shanghai
          env: dev1
        transitive: # 指定需要传递的元数据 key
          - idc
          - env

方式二

# 定义元数据
SCT_METADATA_CONTENT_${key1}=value1 
SCT_METADATA_CONTENT_${key2}=value2

# 指定需要传递的元数据 key
SCT_METADATA_CONTENT_TRANSITIVE=${key1}

方式三

SPI 的方式,待版本支持后补充。

元数据路由原理篇

元数据路由的原理其实非常简单,以 feign 调用为例,例如调用 http://user-serivce/user/get

  1. 根据服务名(user-service)获取目标服务全量实例地址集合
  1. 根据元数据路由规则从全量实例中挑出部分满足规则的实例子集
  • 这一步中,SCT 会执行一次地址 Filter 链,输入是全量地址,输出是经过所有地址 Filter 之后的结果。其中一个 Filter 就是元数据路由 Filter MetadataRouter。执行 Filter 链的调用代码请参考:PolarisLoadBalancerCompositeRule#doRouter
  1. 根据第二步的实例子集,执行负载均衡策略,选中其中一个实例。并替换请求 Url 例如:http://192.168.1.1:8080/user/get
Clone this wiki locally