实现负载均衡-Ribbon

1.服务器端负载均衡:

nginx 是部署在服务器端的

2. 客户端负载均衡:

内容中心相对于用户中心 ,是客户端

 手写一个客户端负载均衡器:

就是从一个list集合中随机选取一个元素作为请求实例

3.使用Ribbon 实现负载均衡:

1.不需要在pom中添加依赖

2. 添加注解:(在RestTemplate 上)

@LoadBalanced

代码:【现在的代码】

不需要拿到用户中心所有实例的信息,在从中选取一个实例

【原来的代码】

当restTemplate 去请求时,ribbon 会自动将user-center 转换成用户中心在 Nacos上的地址,并且对服务端(用户中心)进行负载均衡算法,计算出一个实例进行请求

4.Ribbon内置的负载均衡规则

 

5.ribbon 细粒度配置自定义:

1.Java代码配置:

1.在当前启动类所在包,及所在包下的其他的包 之外 ,创建一个配置类:(配置类所在的包也要自己创建)

@Configuration
public class RibbonConfiguration {

    @Bean
    public IRule ribbonRule(){
        return new RandomRule();
    }
}

这里使用的是RandomRule,随机选择一个server

2. 创建一个 UserCenterConfiguration 类

@Configuration
@RibbonClient(name = "user-center",configuration = RibbonConfiguration.class)
public class UserCenterConfiguration {
}

注意:  configuration = RibbonConfiguration.class

这样就使ribbon的负载均衡规则变成了 :随机选取。

==================================

为什么 RibbonConfiguration 要在启动类所在包之外?

答:  RibbonConfiguration 的@Configuration注解 是一个特殊的@Component,

而在启动类的@SpringBootApplication注解内 有个@ComponentScan 注解(会扫描 启动类所在包,及旗下包的 @Component注解,),若ribbon的配置类若在 启动类包下,则一定会被扫描到【但是ribbon的配置类一定不能被扫描到】。

@SpringBootApplication扫描的上下文叫 主上下文, ribbon的是 子上下文【注意:父子上下文扫描的包一定不能重叠,否则会出现各种问题,例如:spring容器中配置的事务失效】

RibbonConfiguration  类一定要有 @Configuration 注解,但不能被 @SpringBootApplication扫描到,否则 会被所有的@RibbonClient 共享【这里是要对 ribbon 进行细粒度配置,即:客户端 对A服务选择轮训,对 B服务选择随机等等 】

=====================================

 

2.使用配置属性配置:

在 application.yml 中进行配置:

是对 user-center 这个服务进行配置

com.netflix.loadbalancer.RandomRule 是 RandomRule  的全路径地址

3.对比两种配置方式:

 所以:尽量使用属性配置,在同一个服务内 尽量 保持单一性,比如:统一使用 属性配置,不要两种方式混用,增加定位代码的复杂性

6. ribbon 的全局配置:

          还是使用Java代码配置:

1.创建一个RibbonConfiguration 类

@Configuration
public class RibbonConfiguration {

    @Bean
    public IRule ribbonRule(){
        return new RandomRule();
    }
}

2.创建一个

UserCenterConfiguration类【叫什么名字无所谓(沿用上文而已),也可以是Configuration】

注意:这里 是 @RibbonClients 和defaultConfiguration ,没有了 name

7.ribbon的饥饿加载:

 例如:

我们在使用Spring Cloud的Ribbon或Feign来实现服务调用的时候,如果我们的机器或网络环境等原因不是很好的话,有时候会发现这样一个问题:我们服务消费方调用服务提供方接口的时候,第一次请求经常会超时,而之后的调用就没有问题了。下面我们就来说说造成这个问题的原因,以及如何解决的方法。

答:造成第一次服务调用出现失败的原因主要是Ribbon进行客户端负载均衡的Client并不是在服务启动的时候就初始化好的,而是在调用的时候才会去创建相应的Client,所以第一次调用的耗时不仅仅包含发送HTTP请求的时间,还包含了创建RibbonClient的时间,这样一来如果创建时间速度较慢,同时设置的超时时间又比较短的话,很容易就会出现上面所描述的显现。

可以通过饥饿加载来解决:

在yml中:

clients :指定 对 哪个 服务进行 加载 ,多个 用 逗号 隔开 ,不在 clients 内的还是使用懒加载模式

8.扩展Ribbon-权重支持 :

在nacos 控制台可以为每一个实例配置权重.

权重值越大,被调用的几率越大。

把性能差的机器权重设低,性能好的机器权重设高,让请求优先打到性能高的机器上去;

但是ribbon的负载均衡规则 都不支持 Nacos的权重,所以自定义 一个负载均衡规则,支持权重。

1.创建一个NacosWeightedRule类:

@Slf4j
public class NacosWeightedRule extends AbstractLoadBalancerRule {
    @Autowired
    private NacosDiscoveryProperties nacosDiscoveryProperties;
    @Override
    public void initWithNiwsConfig(IClientConfig iClientConfig) {
         //读取配置文件,并初始化NacosWeightedRule
    }
    @Override
    public Server choose(Object o) {
        try {
            BaseLoadBalancer loadBalancer=(BaseLoadBalancer) this.getLoadBalancer();
            //想要请求的微服务的名称
            String name=loadBalancer.getName();
            //实现负载均衡算法,nacos 已经内置了算法
            //拿到服务发现的相关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 ;
        }
    }
}

注意:initWithNiwsConfig方法是用不到的,所以置空,只使用了choose 方法,调用了nacos的基于权重的负载均衡算法

2.在RibbonConfiguration类中new 出来:

@Configuration
public class RibbonConfiguration {

    @Bean
    public IRule ribbonRule(){
        return new NacosWeightedRule();
    }
}

3.使用全局配置:

@Configuration
@RibbonClients(defaultConfiguration  = RibbonConfiguration.class)
public class UserCenterConfiguration {
}
既然Nacos Client已经有负载均衡的能力,Spring Cloud Alibaba为什么还要去整合Ribbon呢?

答:

这主要是为了符合Spring Cloud标准。Spring Cloud Commons有个子项目 spring-cloud-loadbalancer ,该项目制定了标准,用来适配各种客户端负载均衡器(虽然目前实现只有Ribbon,但Hoxton就会有替代的实现了)【但没有基于权重的规则】。

Spring Cloud Alibaba遵循了这一标准,所以整合了Ribbon,而没有去使用Nacos Client提供的负载均衡能力。

9.扩展Ribbon-同一集群优先:

      北京机房的内容中心优先调用 北京机房的用户中心,
      若北京机房找不到任何存活的用户中心,才考虑调用南京机房的用户中心实例

@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;
        }
    }
}

/**
 *   这个是为了调用 Nacos 内置的权重的负载均衡算法 ,
 *   protected static Instance getHostByRandomWeight(List<Instance> hosts)
 *   不能直接调用 getHostByRandomWeight,所以继承 该方法的类,然后调用
 */
class ExtendBalancer extends Balancer {
    public static Instance getHostByRandomWeight2(List<Instance> hosts) {
        return getHostByRandomWeight(hosts);
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_42528855/article/details/115050596
今日推荐