-
Notifications
You must be signed in to change notification settings - Fork 495
服务路由之元数据路由
服务实例通常带有一系列的标签信息,例如实例所属的机房信息、地域信息、环境信息等,这些信息统称为服务实例的元信息。实例注册到注册中心时,会带上自身的元数据信息。当消费方从注册中心获取到实例时,既可以同时获取到每个实例的元信息。
在 Spring Cloud 项目里的 application.yml 中配置以下内容:
spring:
cloud:
tencent:
metadata:
content:
idc: shanghai
env: dev1
应用在启动注册时,会自动读取配置文件并带上 idc=shanghai
和 env=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=shanghai
和 ENV=dev
的元数据。
通过环境变量的方式,paas 平台在拉起一台虚拟机或者一个容器 Pod 时,只需要设置相应的环境变量即可。而应用自身无需做任何其它事情。
以上几种方式,可以通过查看启动日志确认是否正确打标:
grep "Loaded static metadata info" xx.log
前面三种方式为 SCT(Spring Cloud Tencent 的缩写) 内置的方式,但是并不一定符合每个公司自己的规范或者实现。例如:
- 把元数据放到机器上的某一个配置文件里,例如 /etc/metadata
- 启动时,调用 CMDB 的接口获取元信息
所以 SCT 定义了一个 SPI,方便用户自己实现元数据来源。SCT 在注册前,回调 SPI 获取元数据信息,并注册到注册中心。
上一小节介绍了什么是元数据以及元数据的来源,这一小节来介绍一下元数据路由。
既然服务实例都有元数据标签,在某些场景下,服务调用时优先调用到跟主调方具有相同元数据标签的被调方实例。如下图所示:
当 OrderSevice
调用 UserService
时,期望同环境实例优先互相调用。
最典型的实际场景为线下多特性环境:
当 SvcA 和 SvcD 需要在某一次迭代中进行联调时,无需部署一套包含全链路 SvcA,SvcB,SvcC,SvcD 的环境,一方面成本太高,另一方面 SvcB 和 SvcC 可能属于其它团队,不方便部署。 只需要在 dev1 里部署 SvcA 和 SvcD 打上 SCT_METADATA_CONTENT_ENV=dev1
和 SCT_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
。
- 根据服务名(user-service)获取目标服务全量实例地址集合
- 这一步中,SCT 只会返回健康的实例集合。细节可参考源码PolarisLoadBalancer.java
- 根据元数据路由规则从全量实例中挑出部分满足规则的实例子集
- 这一步中,SCT 会执行一次地址 Filter 链,输入是全量地址,输出是经过所有地址 Filter 之后的结果。其中一个 Filter 就是元数据路由 Filter MetadataRouter。执行 Filter 链的调用代码请参考:PolarisLoadBalancerCompositeRule#doRouter
- 根据第二步的实例子集,执行负载均衡策略,选中其中一个实例。并替换请求 Url 例如:
http://192.168.1.1:8080/user/get
- 执行负载均衡调用代码请参考:PolarisLoadBalancerCompositeRule#choose
- 您在使用过程中遇到任何问题,请提 Issue 或者加入我们的开发者群告诉我们,我们会在第一时间反馈
- Spring Cloud Tencent 社区期待您的加入,一个 Star、PR 都是对我们最大的支持
- 项目介绍
- 使用指南
- 最佳实践
- 开发文档
- 学习资料