springcloud-扩展Ribbon支持基于元数据的版本管理
上一篇已经实现了:
-
优先调用同集群下的实例
-
实现基于权重配置的负载均衡
但实际项目,我们可能还会有这样的需求:
-
一个微服务在线上可能多版本共存,例如:
-
服务提供者有两个版本:v1、v2
-
服务消费者也有两个版本:v1、v2
-
v1/v2是不兼容的。服务消费者v1只能调用服务提供者v1;消费者v2只能调用提供者v2。如何实现呢?
下面围绕该场景,实现微服务之间的版本控制。
元数据
元数据就是一堆的描述信息,以map存储。举个例子:
spring:
cloud:
nacos:
metadata:
# 自己这个实例的版本
version: v1
# 允许调用的提供者版本
target-version: v1
- 需求分析,我们需要实现的有两点:
优先选择同集群下,符合metadata的实例
如果同集群加没有符合metadata的实例,就选择所有集群下,符合metadata的实例
写代码
@Slf4j
public class NacosMetadataRule extends AbstractLoadBalancerRule {
@Autowired
private NacosDiscoveryProperties discoveryProperties;
@Override
public void initWithNiwsConfig(IClientConfig clientConfig) {
}
@Override
public Server choose(Object key) {
//--- 负载均衡规则:优先选择同集群下,符合metadata的实例,如果没有,就选择所有集群下,符合metadata的实例----
//获取服务集群
String clusterName=discoveryProperties.getClusterName();
//获取当前服务的原元数据
Map<String, String> metadata = discoveryProperties.getMetadata();
//当前服务可以调用的服务版本
String target_version=metadata.get("target-version");
BaseLoadBalancer loadBalancer= (BaseLoadBalancer) this.getLoadBalancer();
//想要请求的微服务名称
String name =loadBalancer.getName();
NamingService namingService=discoveryProperties.namingServiceInstance();
try {
//所有服务实例
List<Instance> instances = namingService.selectInstances(name, true);
List<Instance> metadataMatchInstances = instances;
//配置了元数据
if(StringUtils.isNotEmpty(target_version)){
metadataMatchInstances = instances.stream()
.filter(instance -> Objects.equals(target_version, instance.getMetadata().get("version")))
.collect(Collectors.toList());
if(CollectionUtils.isEmpty(metadataMatchInstances)){
log.warn("未找到元数据匹配的目标实例!请检查配置。targetVersion = {}, instance = {}", target_version, instances);
return null;
}
}
List<Instance> clusterMetadataMatchInstances = metadataMatchInstances;
//配置了服务集群
if(StringUtils.isNotEmpty(clusterName)){
clusterMetadataMatchInstances= metadataMatchInstances.stream().
filter(instance -> Objects.equals(clusterName,instance.getClusterName())).
collect(Collectors.toList());
if(CollectionUtils.isEmpty(clusterMetadataMatchInstances)){
log.warn("发生跨集群调用。clusterName = {}, targetVersion = {}, clusterMetadataMatchInstances = {}", clusterName, target_version, clusterMetadataMatchInstances);
clusterMetadataMatchInstances=metadataMatchInstances;
}
}
Instance instance=LoadBalancer.getHost(clusterMetadataMatchInstances);
log.debug("选择的instance:port={},instanceId={}",instance.getPort(),instance.getInstanceId());
return new NacosServer(instance);
} catch (NacosException e) {
e.printStackTrace();
}
return null;
}
/**
* 调用权重负载均衡算法 原Balancer方法无法调用
*/
static class LoadBalancer extends Balancer {
public static Instance getHost(List<Instance> hosts) {
return getHostByRandomWeight( hosts);
}
}
}
Q.E.D.