上一篇介绍了,ribbon的组件。本篇要自己写一个灰度方案。其实就是一个很简单的思维扩散。
需求
前端header请求携带version字段。路由服务根据version去需要对应版本的服务集合,进行或轮询或hash或权重的负载。请求路由到服务上,如果还要调用下游服务,也按照version规则去路由下游服务器。前端未携带版本按照后端服务最高version版本进行路由。
分析如果自己动手写一个灰度方案。需要考虑的因素有几点?
- 服务对应的版本。key(版本号):value(对应版本号的服务集合)
- 对应版本号的服务集合需要重新排序。
- 重写负载均衡规则,就是ribbon的IRule方法。按照我们想要的负载规则去路由我们的请求
解决方案:
- 利用注册中心的metadata属性元数据,让服务携带版本信息。
- 拿到要请求的服务集合。spring cloud Alibaba nacos NamingService接口根据服务名称获取所有服务List集合,如果你使用的spring cloud 版本可以使用 ILoadBalancer 对象获取所有的服务集合
- Instance服务里面携带了,服务注册到注册中心的自定义版本信息
- 重写IRule负载规则。按照需求转发请求。
来写一下网关层的实现。
gateway负载规则有一个拦截器
创建负载规则的类信息GrayscaleProperties
public class GrayscaleProperties implements Serializable {
private String version;
private String serverName;
private String serverGroup;
private String active;
private double weight = 1.0D;
}
因为gateway的特殊性LoadBalancerClientFilter过滤器主要解析lb:// 为前缀的路由规则,在通过LoadBalancerClient#choose(String) 方法获取到需要的服务实例,从而实现负载均衡。在这里我们要写自己的负载均衡就需要重新需要重写LoadBalancerClientFilter 过滤器
LoadBalancerClientFilter 介绍:次过滤器作用在url以lb开头的路由,然后利用loadBalancer来获取服务实例,构造目标requestUrl,设置到GATEWAY_REQUEST_URL_ATTR属性中,供NettyRoutingFilter使用。
GatewayLoadBalancerClientAutoConfiguration 在初始化会检测@ConditionalOnBean(LoadBalancerClient.class) 是否存在,如果存在就会加载LoadBalancerClientFilter负载过滤器
以下是源码
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
URI url = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);
String schemePrefix = exchange.getAttribute(GATEWAY_SCHEME_PREFIX_ATTR);
//判断url 前缀 如不是lb开头的就进行下一个过滤器
if (url == null || (!"lb".equals(url.getScheme()) && !"lb".equals(schemePrefix))) {
return chain.filter(exchange);
}
//根据网关的原始网址。替换exchange url为 http://IP:PORT/path 路径的url
//preserve the original url
addOriginalRequestUrl(exchange, url);
log.trace("LoadBalancerClientFilter url before: " url);
// 这里呢会进行调用真正的负载均衡
final ServiceInstance instance = choose(exchange);
if (instance == null) {
String msg = "Unable to find instance for " url.getHost();
if(properties.isUse404()) {
throw new FourOFourNotFoundException(msg);
}
throw new NotFoundException(msg);
}
URI uri = exchange.getRequest().getURI();
// if the `lb:<scheme>` mechanism was used, use `<scheme>` as the default,
// if the loadbalancer doesn't provide one.
String overrideScheme = instance.isSecure() ? "https" : "http";
if (schemePrefix != null) {
overrideScheme = url.getScheme();
}
URI requestUrl = loadBalancer.reconstructURI(new DelegatingServiceInstance(instance, overrideScheme), uri);
log.trace("LoadBalancerClientFilter url chosen: " requestUrl);
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, requestUrl);
return chain.filter(exchange);
}
。。。。
//