栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

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

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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 allInstanceList = namingService.getAllInstances(serviceName, groupName, false);
            // 6、过滤有相同集群的服务实例
            List sameClusterInstanceList = Lists.newlinkedList();
            for (Instance instance : allInstanceList) {
                if (instance.getClusterName().equals(clusterName)) {
                    sameClusterInstanceList.add(instance);
                }
            }
            // 7、过滤相同版本的服务实例
            List 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 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 LIST = Lists.newArrayList();

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

    
    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


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

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/582366.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号