六、Ribbon负载均衡服务调用

  负载均衡服务调用,Ribbon目前也进入维护模式。

一、Ribbon介绍

1.1、概述

  Spring Cloud Ribbon是基于Netflix Ribbon实现的一套基于客户端负载均衡的工具所以Ribbon作用在微服务的服务消费方

  简单的说,Ribbon就是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法和服务调用,Ribbon客户端组件提供一系列完善的配置项如连接超时,重试等。简单的说,就是在配置文件中列出Load Balance(简称LB)后面所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询、随机连接等)去连接这些机器,我们很容易使用Ribbon实现自定义的负载均衡算法。

  简单的说,就是之前通过RestTemplate+@Loadbalanced已经实现简单轮询负载均衡,Ribbon可以修改通过RestTemplate+@Loadbalanced实现负载均衡的规则

1.2、功能作用

  • 负载均衡( LB ):简单的说就是将用户的请求平摊的分配到多个服务器上从而达到系统的HA(高可用)。常见的负载均衡有软件Nginx,LVS。
  • Ribbon本地负载均衡客户端 和Nginx服务端负载均衡区别
    • Nginx是服务器负载均衡,客户端所有请求都会交给Nginx,然后由Nginx实现转发请求。即负载均衡是由服务端实现的。
    • Ribbon本地负载均衡,在调用微服务接口的时候,会在注册中心上获取注册信息服务列表之后缓存到JVM本地,从而在本地实现RPC远程服务调用技术。
  • 负载均衡(LB)
    • 集中式:服务的消费方和提供方之间使用独立的LB设施(硬件F5,nginx),由该设施负责把访问请求通过某种策略转发至服务器。
    • 进程内:将LB逻辑继承到服务消费方,消费方从服务注册列表中心获取有哪些可用地址,然后再从这些地址中选择出一个适合的服务器。Ribbon就是属于进程内LB,它是一个类库,继承于消费方进程,消费方通过他来获取到服务提供方的地址。    

二、Ribbon负载均衡演示

 Ribbon在工作时分成两步

  1. 第一步:先选择EurekaServer,它优先选择在同一区域内负载较少的server。

  2. 第二步:再根据用户指定的策略,在从server取到的服务注册列表中选择一个地址。其中Ribbon提供了多种策略:比如轮询、随机和根据响应时间加权。

  总结:Ribbon其实就是一个软负载均衡的客户端组件,他可以和其他所需请求的客户端结合使用,和eureka结合只是其中的一个实例

  pom.xml添加依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>

  spring-cloud-starter-netflix-eureka-client自带了Spring-cloud-starter-ribbon引用。所以Ribbon引用可以省略

  RestTemplate的使用

三、IRule

1、Ribbon核心组件IRule是根据特定算法从服务列表中选取一个要访问的服务

算法说明
com.netflix.loadbalancer.RoundRobinRule 轮询
com.netflix.loadbalancer.RandomRule 随机
com.netflix.loadbalancer.RetryRule 先按照RoundRobinRule的策略获取服务,如果获取服务失败则在指定时间内会进行重试
WeightedResponseTimeRule 对RoundRobinRule的扩展,响应速度越快的实例选择权重越大,越容易被选择
BestAvailableRule 会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务
AvailabilityFilteringRule 先过滤掉故障实例,再选择并发较小的实例
ZoneAvoidanceRule 默认规则,复合判断server所在区域的性能和server的可用性选择服务器

2、修改负载均衡规则

  修改cloud-consumer-order80

  • 注意配置细节
    这个自定义配置类不能放在主启动类@ComponentScan所扫描的当前包下以及子包下,否则我们自定义的这个配置类就会被所有的Ribbon客户端所共享,达不到特殊定制的目的了。
  • 新建JdyRule规则类。
@Configuration
public class JdyRule {

    @Bean
    public IRule myRule(){
        return new RandomRule();//定义为随机
    }
}

  主启动类添加@RibbonClient注解,实现使用Ribbon代替RestTemplate+@Loadbalanced的负载均衡规则。

@EnableEurekaClient
@SpringBootApplication
@RibbonClient(name = "CLOUD-PAYMENT-SERVICE",configuration = JdyRule.class)
public class OrderMain80 {
    public static void main(String[] args) {
        SpringApplication.run(OrderMain80.class,args);
    }
}

四、Ribbon负载均衡算法

4.1、原理  

  负载均衡算法:rest接口第几次请求数%服务器集群总数量=实际调用服务器位置下标,每次服务重启后rest接口计数从1开始。

List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
如下:
    List[0] instance = 127.0.0.1:8081
    List[1] instance = 127.0.0.1:8082
        8081和8082组成集群,它们共计2台机器,集群总数为2,按照轮询算法原则:
        当总请求数为1时:1%2=1对应下标位置为1,则获得的服务器地址为127.0.0.1:8081
        当总请求数为1时:2%2=1对应下标位置为0,则获得的服务器地址为127.0.0.1:8082
        当总请求数为1时:3%2=1对应下标位置为1,则获得的服务器地址为127.0.0.1:8081
        当总请求数为1时:4%2=1对应下标位置为0,则获得的服务器地址为127.0.0.1:8082
        以此类推....

4.2、自定义负载均衡器

1、环境准备

  • 8001/8002微服务controller加  
@GetMapping(value = "/payment/lb")
public String getPaymentLB(){
    return serverPort;
}
  • 80订单微服务改造:ApplicationContextBean去掉@LoadBalanced和@RibbonClient

2、编写自定义负载均衡器

  • 定义接口
public interface LoadBalancer {
     //收集服务器总共有多少台能够提供服务的机器,并放到list里面
    ServiceInstance instances(List<ServiceInstance> serviceInstances);
}
  • 参考Ribbon轮询源码实现MyLB
@Component
public class MyLB implements LoadBalancer {

    private AtomicInteger atomicInteger = new AtomicInteger(0);

    //获得访问序号
    private final int getAndIncrement(){
        int current;
        int next;
        do {
            current = this.atomicInteger.get();
            next = current >= 2147483647 ? 0 : current + 1;
             //第一个参数是期望值,第二个参数是修改值是
        }while (!this.atomicInteger.compareAndSet(current,next)); 
        System.out.println("*******第几次访问,次数next: "+next);
        return next;
    }

    @Override
    //得到访问的服务器实例。
    public ServiceInstance instances(List<ServiceInstance> serviceInstances) {  
       int index = getAndIncrement() % serviceInstances.size(); 
        return serviceInstances.get(index);
    }
}
  • 80订单微服务controller
@RestController
@Slf4j
public class OrderController {

    public static final String PAYMENT_URL = "http://CLOUD-PAYMENT-SERVICE";

    @Autowired
    private RestTemplate restTemplate;


    @Autowired
    private DiscoveryClient client;

    @Autowired
    private LoadBalance loadBalance;


    /**
     * 自定义负载均衡器
     * @return
     */
    @GetMapping(value = "/consumer/payment/lb")
    public String getPaymentLB(){
        List<ServiceInstance> instances = client.getInstances("CLOUD-PAYMENT-SERVICE");
        if (instances == null || instances.size() <= 0){
            return null;
        }
        ServiceInstance serviceInstance = loadBalance.instances(instances);
        URI uri = serviceInstance.getUri();
        return restTemplate.getForObject(uri+"/payment/lb",String.class);
    }
    
}

3、测试

  访问:http://localhost/consumer/payment/lb

posted @ 2021-03-18 11:50  jingdy  阅读(78)  评论(0)    收藏  举报