zoukankan      html  css  js  c++  java
  • 2.【Spring Cloud Alibaba】实现负载均衡-Ribbon

    负载均衡的两种方式

    如何实现负载均衡

    目前已经实现让A总能找到B,如何实现负载均衡

    image

    负载均衡的两种方式

    • 服务器端负载均衡
    • 客户端负载均衡

    image

    image

    使用Ribbo实现负载均衡

    Ribbon是什么

    Netflix开源的客户端侧负载均衡器

    引入Ribbon后的架构演进

    image

    整合Ribbon实现负载均衡

    // 在spring容器中,创建一个对象,类型RestTemplate;名称/ID是:restTemplate
    // <bean id="restTemplate" class="xxx.RestTemplate"/>
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        RestTemplate template = new RestTemplate();
        return template;
    }
    
    @Autowired
    private RestTemplate restTemplate;
    
    @GetMapping("/test-rest-template-sentinel/{userId}")
    public UserDTO test(@PathVariable Integer userId) {
        return this.restTemplate
            .getForObject(
                "http://user-center/users/{userId}",
                UserDTO.class, userId);
    }
    

    Ribbon的组成

    image

    Ribbon内置的负载均衡规则

    image

    细粒度配置自定义

    • Java代码配置
    • 用配置属性配置
    • 最佳实践总结
    Java代码配置
    @Configuration
    @RibbonClient(name = "user-center",configuration = RibbonConfiguration.class)
    public class UserCenterRibbonConfiguration {
    }
    
    @Configuration
    public class RibbonConfiguration {
        @Bean
        public IRule ribbonRule() {
            return new NacosSameClusterWeightedRule();
        }
    }
    
    用配置属性配置

    image

    user-center:
      ribbon:
        NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
    

    两种方式对比

    image

    Ribbon全局配置

    @Configuration
    @RibbonClients(defaultConfiguration = RibbonConfiguration.class)
    public class UserCenterRibbonConfiguration {
    }
    
    @Configuration
    public class RibbonConfiguration {
        @Bean
        public IRule ribbonRule() {
            return new NacosSameClusterWeightedRule();
        }
    }
    

    Ribbon饥饿加载配置方式

    ribbon:
      eager-load:
        enabled: true
        clients: user-center
    

    扩展Ribbon-支持Nacos权重

    import com.alibaba.nacos.api.exception.NacosException;
    import com.alibaba.nacos.api.naming.NamingService;
    import com.alibaba.nacos.api.naming.pojo.Instance;
    import com.netflix.client.config.IClientConfig;
    import com.netflix.loadbalancer.*;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.cloud.alibaba.nacos.NacosDiscoveryProperties;
    import org.springframework.cloud.alibaba.nacos.ribbon.NacosServer;
    
    @Slf4j
    public class NacosWeightedRule extends AbstractLoadBalancerRule {
        @Autowired
        private NacosDiscoveryProperties nacosDiscoveryProperties;
        @Override
        public void initWithNiwsConfig(IClientConfig clientConfig) {
            // 读取配置文件,并初始化NacosWeightedRule
        }
    
        @Override
        public Server choose(Object key) {
            try {
                BaseLoadBalancer loadBalancer = (BaseLoadBalancer) this.getLoadBalancer();
    //        log.info("lb = {}", loadBalancer);
    
                // 想要请求的微服务的名称
                String name = loadBalancer.getName();
    
                // 拿到服务发现的相关API
                NamingService namingService = nacosDiscoveryProperties.namingServiceInstance();
    
                // nacos client自动通过基于权重的负载均衡算法,给我们选择一个实例。
                Instance instance = namingService.selectOneHealthyInstance(name);
    
                log.info("选择的实例是:port = {}, instance = {}", instance.getPort(), instance);
                return new NacosServer(instance);
            } catch (NacosException e) {
                return null;
            }
        }
    }
    
    // spring cloud commons --> 定义了标准
    // spring cloud loadbalancer --> 没有权重
    

    扩展Ribbon-同一集群优先调用

    import com.alibaba.nacos.api.exception.NacosException;
    import com.alibaba.nacos.api.naming.NamingService;
    import com.alibaba.nacos.api.naming.pojo.Instance;
    import com.alibaba.nacos.client.naming.core.Balancer;
    import com.netflix.client.config.IClientConfig;
    import com.netflix.loadbalancer.AbstractLoadBalancerRule;
    import com.netflix.loadbalancer.BaseLoadBalancer;
    import com.netflix.loadbalancer.Server;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.cloud.alibaba.nacos.NacosDiscoveryProperties;
    import org.springframework.cloud.alibaba.nacos.ribbon.NacosServer;
    import org.springframework.util.CollectionUtils;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Objects;
    import java.util.stream.Collectors;
    
    @Slf4j
    public class NacosSameClusterWeightedRule extends AbstractLoadBalancerRule {
        @Autowired
        private NacosDiscoveryProperties nacosDiscoveryProperties;
    
        @Override
        public void initWithNiwsConfig(IClientConfig clientConfig) {
    
        }
    
        @Override
        public Server choose(Object key) {
            try {
                // 拿到配置文件中的集群名称 BJ
                String clusterName = nacosDiscoveryProperties.getClusterName();
    
                BaseLoadBalancer loadBalancer = (BaseLoadBalancer) this.getLoadBalancer();
                // 想要请求的微服务的名称
                String name = loadBalancer.getName();
    
                // 拿到服务发现的相关API
                NamingService namingService = nacosDiscoveryProperties.namingServiceInstance();
    
                // 1. 找到指定服务的所有实例 A
                List<Instance> instances = namingService.selectInstances(name, true);
    
                // 2. 过滤出相同集群下的所有实例 B
                List<Instance> sameClusterInstances = instances.stream()
                    .filter(instance -> Objects.equals(instance.getClusterName(), clusterName))
                    .collect(Collectors.toList());
    
                // 3. 如果B是空,就用A
                List<Instance> instancesToBeChosen = new ArrayList<>();
                if (CollectionUtils.isEmpty(sameClusterInstances)) {
                    instancesToBeChosen = instances;
                    log.warn("发生跨集群的调用, name = {}, clusterName = {}, instances = {}",
                        name,
                        clusterName,
                        instances
                    );
                } else {
                    instancesToBeChosen = sameClusterInstances;
                }
                // 4. 基于权重的负载均衡算法,返回1个实例
                Instance instance = ExtendBalancer.getHostByRandomWeight2(instancesToBeChosen);
                log.info("选择的实例是 port = {}, instance = {}", instance.getPort(), instance);
    
                return new NacosServer(instance);
            } catch (NacosException e) {
                log.error("发生异常了", e);
                return null;
            }
        }
    }
    
    class ExtendBalancer extends Balancer {
        public static Instance getHostByRandomWeight2(List<Instance> hosts) {
            return getHostByRandomWeight(hosts);
        }
    }
    

    扩展Ribbon-基于元数据的版本控制

    元数据

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

    spring:
      cloud:
        nacos:
            metadata: 
              # 自己这个实例的版本
              version: v1
              # 允许调用的提供者版本
              target-version: v1
    
    代码实现
    @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);
        }
    }
    

    深入理解Nacos的Namespace

    服务调用不能跨namespace

  • 相关阅读:
    opencvcircle圆
    关于“100g文件全是数组,取最大的100个数”解决方法汇总
    软件测试的核心价值
    写给测试新手
    软件测试职业规划
    关于软件测试职业规划的讨论
    云计算将改变传统软件测试行业
    对MVC模式的理解
    软件测试专家于涌谈行业前景与测试人员成长
    MVC模式
  • 原文地址:https://www.cnblogs.com/xjknight/p/12349096.html
Copyright © 2011-2022 走看看