SpringCloud(10) 自定义负载均衡策略

一、前言

本文将自定义负载均衡策略来实现权限策略同一集群优先使用带版本策略,主要解决在本地开发环境启动相同服务时,调用服务会跑到其他人那里。

简要逻辑:在通过nacos注册服务时,添加version参数绑定本地服务ip信息,在服务调用时,去获取nacos上注册服务信息,通过version参数条件去指定调用具体服务。
在这里插入图片描述

ex: 服务A调用服务B
如果服务B的version参数值和服务A的version参数值一致,即会优先调用与服务A相同version值的服务B
如果没有与服务A相同version值的服务B,再去随机按权重调用服务B
在这里插入图片描述

环境:

  1. spring-boot-dependencies 2.3.2.RELEASE
  2. spring-cloud-dependencies Hoxton.SR8
  3. spring-cloud-alibaba-dependencies 2.2.3.RELEASE

二、自定义负载均衡策略

在这里插入图片描述

1、自定义负载均衡策略-权重

@Slf4j
public class BalancerWeightRule extends AbstractLoadBalancerRule {
    
    

    @Autowired
    private NacosServiceManager nacosServiceManager;

    @Autowired
    private NacosDiscoveryProperties nacosDiscoveryProperties;

    @Override
    public void initWithNiwsConfig(IClientConfig iClientConfig) {
    
    
        // 读取配置文件, 并且初始化, ribbon内部基本上用不上
    }

    /**
     * 这个方法是实现负载均衡策略的方法
     */
    @Override
    public Server choose(Object key) {
    
    
        try {
    
    
            // 1、获取当前服务的分组名称
            String groupName = this.nacosDiscoveryProperties.getGroup();
            // 2、获取当前服务的负载均衡器
            BaseLoadBalancer baseLoadBalancer = (BaseLoadBalancer) this.getLoadBalancer();
            // 3、获取目标服务的服务名
            String serviceName = baseLoadBalancer.getName();
            // 4、获取nacos提供的服务注册api
            NamingService namingService = this.nacosServiceManager.getNamingService(this.nacosDiscoveryProperties.getNacosProperties());
            // 5、根据目标服务名称和分组名称去获取服务实例,nacos实现了权重的负载均衡算法  false: 及时获取nacos注册服务信息
            Instance toBeChooseInstance = namingService.selectOneHealthyInstance(serviceName, groupName, false);
            BalancerInstanceUtil.printInstance(BalancerRuleTypeEnum.WEIGHT, toBeChooseInstance);
            return new NacosServer(toBeChooseInstance);
        } catch (NacosException e) {
    
    
            log.error("自定义负载均衡策略-权重 调用异常: ", e);
            return null;
        }
    }

}

2、自定义负载均衡策略-同一集群优先使用带版本实例

@Slf4j
public class BalancerVersionRule extends AbstractLoadBalancerRule {
    
    

    @Autowired
    private NacosServiceManager nacosServiceManager;

    @Autowired
    private NacosDiscoveryProperties nacosDiscoveryProperties;

    @Override
    public void initWithNiwsConfig(IClientConfig iClientConfig) {
    
    

    }

    @Override
    public Server choose(Object o) {
    
    
        try {
    
    
            // 1、获取当前服务的分组名称、集群名称、版本号
            String groupName = this.nacosDiscoveryProperties.getGroup();
            String clusterName = this.nacosDiscoveryProperties.getClusterName();
            String version = this.nacosDiscoveryProperties.getMetadata().get("version");
            // 2、获取当前服务的负载均衡器
            BaseLoadBalancer baseLoadBalancer = (BaseLoadBalancer) this.getLoadBalancer();
            // 3、获取目标服务的服务名
            String serviceName = baseLoadBalancer.getName();
            // 4、获取nacos提供的服务注册api
            NamingService namingService = this.nacosServiceManager.getNamingService(this.nacosDiscoveryProperties.getNacosProperties());
            // 5、获取所有服务名为serviceName的服务实例   false: 及时获取nacos注册服务信息
            List<Instance> allInstanceList = namingService.getAllInstances(serviceName, groupName, false);
            // 6、过滤有相同集群的服务实例
            List<Instance> sameClusterInstanceList = Lists.newLinkedList();
            for (Instance instance : allInstanceList) {
    
    
                if (instance.getClusterName().equals(clusterName)) {
    
    
                    sameClusterInstanceList.add(instance);
                }
            }
            // 7、过滤相同版本的服务实例
            List<Instance> sameVersionInstanceList = Lists.newLinkedList();
            for (Instance instance : sameClusterInstanceList) {
    
    
                if (version.equals(instance.getMetadata().get("version"))) {
    
    
                    sameVersionInstanceList.add(instance);
                }
            }
            // 8、选择合适的服务实例
            Instance toBeChooseInstance;
            if (CollectionUtils.isEmpty(sameVersionInstanceList)) {
    
    
                toBeChooseInstance = WeightedBalancer.chooseInstanceByRandomWeight(sameClusterInstanceList);
                BalancerInstanceUtil.printInstance(BalancerRuleTypeEnum.VERSION_WEIGHT, toBeChooseInstance);
            } else {
    
    
                toBeChooseInstance = WeightedBalancer.chooseInstanceByRandomWeight(sameVersionInstanceList);
                BalancerInstanceUtil.printInstance(BalancerRuleTypeEnum.VERSION, toBeChooseInstance);
            }
            return new NacosServer(toBeChooseInstance);
        } catch (NacosException e) {
    
    
            log.error("Nacos同一集群带版本优先调用异常: ", e);
            return null;
        }
    }

}

版本策略所需随机权重类

public class WeightedBalancer extends Balancer {
    
    

    public static Instance chooseInstanceByRandomWeight(List<Instance> instanceList) {
    
    
        // 这是父类Balancer自带的根据随机权重获取服务的方法.
        return getHostByRandomWeight(instanceList);
    }

}

3、启用自定义负载均衡策略

可参考 https://cloud.spring.io/spring-cloud-static/Dalston.SR5/multi/multi_spring-cloud-ribbon.html#_customizing_the_ribbon_client

@Configuration
@RibbonClients(defaultConfiguration = GlobalRibbonConfig.class)
public class CustomRibbonConfig {
    
    

}
@Slf4j
@Data
@Configuration
public class GlobalRibbonConfig {
    
    

    @Value("${ribbon.rule-type:}")
    private String ruleType;

    @Bean
    public IRule getRule() {
    
    
        // 自定义负载均衡策略
        BalancerRuleTypeEnum balancerRuleTypeEnum = BalancerRuleTypeEnum.getEnum(this.ruleType);
        log.info("使用自定义负载均衡策略:[{}]", balancerRuleTypeEnum.getDesc());
        switch (balancerRuleTypeEnum) {
    
    
            case WEIGHT:
                // 权重
                return new BalancerWeightRule();
            case VERSION:
                // 同一集群优先带版本实例
                return new BalancerVersionRule();
            default:
                // 默认权重
                return new BalancerWeightRule();
        }
    }

}

通过配置ribbon.rule-type参数值来选择使用哪一种负载均衡策略

ribbon:
  rule-type: version

4、负载均衡策略枚举类和工具类

@Slf4j
@Getter
@AllArgsConstructor
public enum BalancerRuleTypeEnum {
    
    

    /**
     * 权重
     */
    WEIGHT("weight", "权重"),
    /**
     * 同一集群使用相同版本
     */
    VERSION("version", "同一集群使用相同版本"),
    /**
     * 同一集群无相同版本,使用权重
     */
    VERSION_WEIGHT("version_weight", "同一集群无相同版本,使用权重");

    /**
     * 规则类型
     */
    private final String type;
    /**
     * 规则描述
     */
    private final String desc;

    private static final List<BalancerRuleTypeEnum> LIST = Lists.newArrayList();

    static {
    
    
        LIST.addAll(Arrays.asList(BalancerRuleTypeEnum.values()));
    }

    /**
     * 根据指定的规则类型查找相应枚举类
     *
     * @param type 规则类型
     * @return 规则类型枚举信息
     * @author zhengqingya
     * @date 2020/9/13 18:46
     */
    public static BalancerRuleTypeEnum getEnum(String type) {
    
    
        for (BalancerRuleTypeEnum itemEnum : LIST) {
    
    
            if (itemEnum.getType().equals(type)) {
    
    
                return itemEnum;
            }
        }
        log.warn("未找到指定的负载均衡策略,默认权重策略!");
        return WEIGHT;
    }

}
@Slf4j
public class BalancerInstanceUtil {
    
    

    public static void printInstance(BalancerRuleTypeEnum ruleTypeEnum, Instance instance) {
    
    
        if (instance == null) {
    
    
            return;
        }
        log.info("自定义负载均衡策略-[{}] serviceName: [{}], clusterName: [{}], ip: [{}] port: [{}]",
                ruleTypeEnum.getDesc(),
                instance.getServiceName(),
                instance.getClusterName(),
                instance.getIp(),
                instance.getPort()
        );
    }

}

三、配置ribbon饥饿加载模式

主要解决:在服务启动后,第1次访问可能会报错
产生原因:ribbon服务调用默认使用懒加载模式,即在调用的时候才会去创建相应的client

ribbon:
  # 配置饥饿加载模式
  eager-load:
    # 开启饥饿加载模式
    enabled: true
    # 指定需要饥饿加载的服务名
    clients:
      - "demo"
      - "system"
      - "tool"

四、本文案例demo源码

https://gitee.com/zhengqingya/small-tools


今日分享语句:
我不敢休息,因为我没有存款。我不敢说累,因为我没有成就。我不敢偷懒,因为我还要生活。我能放弃选择,但是我不能选择放弃。坚强拼搏是我唯一的选择。

猜你喜欢

转载自blog.csdn.net/qq_38225558/article/details/121452527