SpringCloud灰度发布
一: 调用链分析
请求==>网关==>服务Resttemplate调用==>服务
请求==>网关==>服务Fegin调用==>服务
无论是通过Resttemplate还是Fegin的方式进行服务间的调用,他们都会从Ribbon选择一个服务实例返回
二:调用链详细步骤
-
用户请求首先到达Nginx然后转发到网关,此时网关拦截器会根据用户携带请求token解析出对应的userId
-
网关从配置中心拉取灰度用户列表,然后根据灰度用户策略判断该用户是否是灰度用户。如是,则给该请求添加请求头及线程变量添加信息version=xxx;若不是,则不做任何处理放行
-
在网关拦截器执行完毕后,网关在进行转发请求时会通过负载均衡器Ribbon。
-
负载均衡Ribbon被重写。当请求到达时候,Ribbon会取出网关存入线程变量值
version。于此同时,Ribbon还会取出所有缓存的服务列表(定期从eureka刷新获取最新列表)及其该服务的metadata-map信息。然后取出服务metadata-map的version信息与线程变量version进行判断对比,若值一直则选择该服务作为返回。若所有服务列表的version信息与之不匹配,则返回null,此时Ribbon选取不到对应的服务则会报错! -
当服务为非灰度服务,即没有version信息时,此时Ribbon会收集所有非灰度服务列表,然后利用Ribbon默认的规则从这些非灰度服务列表中返回一个服务。
-
网关通过Ribbon将请求转发到consumer服务后,可能还会通过fegin或resttemplate调用其他服务,如provider服务。但是无论是通过fegin还是resttemplate,他们最后在选取服务转发的时候都会通过Ribbon。
-
那么在通过fegin或resttemplate调用另外一个服务的时候需要设置一个拦截器,将请求头version=xxx给带上,然后存入线程变量。
-
在经过fegin或resttemplate 的拦截器后最后会到Ribbon,Ribbon会从线程变量里面取出version信息。然后重复步骤(4)和(5)
小结
- 将注册中心的灰度服务打上标识
eureka可以使用eureka.instance.metadata-map配置
备注:
Eureka的元数据有两种,分别为标准元数据和自定义元数据。
标准元数据:主机名、IP地址、端口号、状态页和健康检查等信息,这些信息都会被发布在服务注册表中,用于服务之间的调用。
自定义元数据:自定义元数据可以使用eureka.instance.metadata-map配置,这些元数据可以在远程客户端中访问,但是一般不会改变客户端的行为,除非客户端知道该元数据的含义
- 请求到达网关时获取灰度用户,对应的灰度用户在请求头里设置上灰度标识
SpringGateWay过滤器GlobalFilter
Dubbo过滤器Filter
Zuul过滤器ZuulFilter
- 路由或者负载均衡器获取服务列时,根据灰度标识,获取对应的服务
resttemplate拦截器ClientHttpRequestInterceptor
fegin拦截器RequestInterceptor
Ribbon自定义负载均衡ZoneAvoidanceRule
Ribbon详细扩展点可参考前面文章
public class GrayMetadataRule extends ZoneAvoidanceRule {
// 略....
@Override
public Server choose(Object key) {
//1.从线程变量获取请求头里获取version信息
String version = xxx;
//2.获取服务实例列表
List<Server> serverList = this.getPredicate().getEligibleServers(this.getLoadBalancer().getAllServers(), key);
//3.循环serverList,选择version匹配的服务并返回
for (Server server : serverList) {
Map<String, String> metadata = ((DiscoveryEnabledServer) server).getInstanceInfo().getMetadata();
String metaVersion = metadata.get("version);
if (!StringUtils.isEmpty(metaVersion)) {
if (metaVersion.equals(hystrixVer)) {
return server;
}
}
}
}
}