扩展Ribbon支持基于元数据的版本管理

    技术2022-07-11  111

    一个微服务在线上可能多版本共存,例如:

    服务提供者有两个版本:v1、v2服务消费者也有两个版本:v1、v2

    v1/v2是不兼容的。

    1、服务消费者v1只能调用服务提供者v1;消费者v2只能调用提供者v2。

    2、优先调用同集群下的实例。

    3、实现基于权重配置的负载均衡。

    如何实现呢?

    下面围绕该场景,实现微服务之间的版本控制。

    元数据

    元数据就是一堆的描述信息,以map存储。举个例子:

    spring: cloud: nacos: metadata: # 自己这个实例的版本 version: v1 # 允许调用的提供者版本 target-version: v1

    需求分析

    我们需要实现的有两点:

    优先选择同集群下,符合metadata的实例如果同集群加没有符合metadata的实例,就选择所有集群下,符合metadata的实例

    写代码

    @Slf4j public class NacosFinalRule extends AbstractLoadBalancerRule { @Autowired private NacosDiscoveryProperties nacosDiscoveryProperties; @Override public Server choose(Object key) { // 负载均衡规则:优先选择同集群下,符合metadata的实例 // 如果没有,就选择所有集群下,符合metadata的实例 // 1. 查询所有实例 A // 2. 筛选元数据匹配的实例 B // 3. 筛选出同cluster下元数据匹配的实例 C // 4. 如果C为空,就用B // 5. 随机选择实例 try { String clusterName = this.nacosDiscoveryProperties.getClusterName(); String targetVersion = this.nacosDiscoveryProperties.getMetadata().get("target-version"); DynamicServerListLoadBalancer loadBalancer = (DynamicServerListLoadBalancer) getLoadBalancer(); String name = loadBalancer.getName(); NamingService namingService = this.nacosDiscoveryProperties.namingServiceInstance(); // 所有实例 List<Instance> instances = namingService.selectInstances(name, true); List<Instance> metadataMatchInstances = instances; // 如果配置了版本映射,那么只调用元数据匹配的实例 if (StringUtils.isNotBlank(targetVersion)) { metadataMatchInstances = instances.stream() .filter(instance -> Objects.equals(targetVersion, instance.getMetadata().get("version"))) .collect(Collectors.toList()); if (CollectionUtils.isEmpty(metadataMatchInstances)) { log.warn("未找到元数据匹配的目标实例!请检查配置。targetVersion = {}, instance = {}", targetVersion, instances); return null; } } List<Instance> clusterMetadataMatchInstances = metadataMatchInstances; // 如果配置了集群名称,需筛选同集群下元数据匹配的实例 if (StringUtils.isNotBlank(clusterName)) { clusterMetadataMatchInstances = metadataMatchInstances.stream() .filter(instance -> Objects.equals(clusterName, instance.getClusterName())) .collect(Collectors.toList()); if (CollectionUtils.isEmpty(clusterMetadataMatchInstances)) { clusterMetadataMatchInstances = metadataMatchInstances; log.warn("发生跨集群调用。clusterName = {}, targetVersion = {}, clusterMetadataMatchInstances = {}", clusterName, targetVersion, clusterMetadataMatchInstances); } } Instance instance = ExtendBalancer.getHostByRandomWeight2(clusterMetadataMatchInstances); return new NacosServer(instance); } catch (Exception e) { log.warn("发生异常", e); return null; } } @Override public void initWithNiwsConfig(IClientConfig iClientConfig) { } }

    负载均衡算法:

    public class ExtendBalancer extends Balancer { /** * 根据权重,随机选择实例 * * @param instances 实例列表 * @return 选择的实例 */ public static Instance getHostByRandomWeight2(List<Instance> instances) { return getHostByRandomWeight(instances); } }

     

    Processed: 0.029, SQL: 9