文章目录

  • 1、什么是Ribbon
  • 2、工作方式
  • 3、使用Ribbon
  • 3.1 RestTemplate
  • 3.2 Ribbon 常用的负载均衡算法
  • 3.3 切换负载均衡算法
  • 4、自定义负载均衡算法
  • 5、停更声明


1、什么是Ribbon

  Github地址:https://github.com/Netflix/ribbon

  SpringCloud Ribbon 是基于 Netflix Ribbon 实现的一套客户端负载均衡工具。

  简单来说,Ribbon 是 Netflx 发布的开源项目,主要功能是提供客户端的软件负载均衡算法和服务调用。

  Ribbon 客户端组件提供一系列完善的配置项,如:连接超时、重试等。

  简单地说,就是在配置文件中列出 LoadBalancer(简称 LB)后面所有的机器,Ribbon 会自动帮助你基于某种规则(轮询、随机连接)去连接这些机器,我们很容易使用 Ribbon 实现自定义的负载均衡算法。

  那什么叫做本地负载均衡呢?

  负载均衡分两大类:

  一类是集中式LB,即在服务的消费方和提供方之间使用独立的 LB 设施(可以是硬件,如 F5,也可以是软件,如 nginx),由这个 LB 设施负责把访问请求通过某种策略转发到服务的提供方;

  另一类是进程内LB,即把 LB 设施集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己从这些可用地址中选出一个合适的。

  Ribbon 就属于进程内 LB,它只是一个类库,集成于消费方进程,消费方通过它来获取服务提供方的地址。

  我们的消费方模块常用RestTemplate来访问服务方,Ribbon = 负载均衡 + RestTemplate。

  总的来说,Ribbon 其实就是软负载均衡的客户端组件,它可以和其他所需请求的客户端结合使用,和 Eureka 结合知识其中的一个实例。

springcloud根据excel进行多语言配置_spring

2、工作方式

  Ribbon 在工作时分成两步:
  1、选择 Eureka Server(Eureka 也是集群,选择其中一个),优先选择在同一个区域中负载较少的 server;
  2、根据用户指定的策略,从 server 取到的服务注册列表中选择一个地址。

  Ribbon 提供了多种选择地址策略,如轮询、随机、根据响应时间加权等。

3、使用Ribbon

  我们使用 Ribbon 不用另外导入 pom,因为在 Eureka 中已经继承了 Ribbon:

springcloud根据excel进行多语言配置_spring_02


  假设我们有一个 80 端口的 order 模块是消费方,有 8001 和 8002 端口的两个 payment 服务方模块。

  我们需要在 80 端口的 order 模块中使用 Ribbon 来访问 payment 模块,让 Ribbon 决定是选择 8001 还是 8002。

3.1 RestTemplate

  讲 Ribbon 如何使用之前先介绍 RestTemplate,它是org.springframework.web.client包下的一个类,简单来说,RestTemplate用于在消费方模块访问服务费模块使用,比如 80 端口模块使用 post 方式访问 8001 模块,可以使用restTemplate.postForObject()方法,需要 get 方式,就可以用restTemplate.getForEntity方法等。

  这里重点说明两个方法:getForObject()getForEntity

springcloud根据excel进行多语言配置_ribbon_03


springcloud根据excel进行多语言配置_java_04


  也就是说,Restemplate 的 xxxForObject() 方法返回的是响应体中的数据,xxxForEntity() 方法返回的是 entity 对象,这个对象不仅包含响应体,还包含状态码、响应头等。

3.2 Ribbon 常用的负载均衡算法

  Ribbon 使用 IRule 接口,根据特定算法从所有服务中,选择一个服务,这个接口有 7 个实现类,每个实现类代表一个负载均衡算法:

springcloud根据excel进行多语言配置_ribbon_05


  整理如下:

springcloud根据excel进行多语言配置_springcloud_06


  我们在使用 RestTemplate 时,会在配置 Bean 中加上 @LoadBalanced 注解,然后 Ribbon 就会选择默认规则 ZoneAvoidanceRule。

package pers.klb.springcloud.config;

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class ApplicationContextConfig {

    @Bean
    @LoadBalanced   // 负载均衡注解
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
}

3.3 切换负载均衡算法

  Ribbon 默认选择的是 ZoneAvoidanceRule,我们更换算法的步骤如下:

  1、定义配置类,这个配置类不能被 SpringBoot 主启动类扫描到,所以我们不和主启动类放在一个包下:

springcloud根据excel进行多语言配置_spring_07


  配置类内如为:

package pers.klb.myrule;

import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @program: SpringCloud
 * @description: 自定义负载均衡算法
 * @author: Meumax
 * @create: 2020-07-29 10:20
 **/
@Configuration
public class MySelfRule {

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

  里面就定义一个 Bean ,返回的是随机算法。下一步就是把这个 Bean 注入到 Ribbon 当中,在主启动类添加注解@RibbonClient,指定这个Bean:

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

  name 指的是服务方的实例名称,访问这个服务时才调用这个负载均衡算法。

4、自定义负载均衡算法

  先定义一个接口:

public interface LoadBalancer {
    /**
     * 根据所有的服务,选择一个本次调用的服务
     * @param serviceInstances
     * @return
     */
    ServiceInstance instances(List<ServiceInstance> serviceInstances);
}

  接口的实现类:

@Component
public class MyLB implements LoadBalancer {

    private AtomicInteger atomicInteger = new AtomicInteger(0);

    /**
     * 获取下一个要调用服务的 id
     *
     * @return
     */
    public final int getAdnIncrement() {
        int current;
        int next;
        do {
            current = this.atomicInteger.get();
            next = current >= 2147483647 ? 0 : current + 1;
        } while (!this.atomicInteger.compareAndSet(current, next)); // 调用 cas,进行自旋锁,每次 next+1
        System.out.println("*****next:" + next);
        return next;
    }

    @Override
    public ServiceInstance instances(List<ServiceInstance> serviceInstances) {
        int index = getAdnIncrement() % serviceInstances.size();
        return serviceInstances.get(index);
    }
}

  80 端口模块的控制器:

@RestController
@Slf4j
public class OrderController {
    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private LoadBalancer loadBalancer;

    @Autowired
    private DiscoveryClient discoveryClient;

	@GetMapping("/consumer/payment/lb")
    public String getPaymentLB() {
        List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
        if (instances == null || instances.size() <= 0) return null;
        ServiceInstance serviceInstance = loadBalancer.instances(instances);
        URI uri = serviceInstance.getUri();
        return restTemplate.getForObject(uri + "/payment/lb", String.class);
    }
}

  工作逻辑很简单,先通过DiscoveryClient获取服务名为 CLOUD-PAYMENT-SERVICE 的所有实例,是一个 List,然后把这个实例list放到LoadBalancer中,我们自定义的算法就会从这个List中选择其中一个实例,这时就完成了负载均衡的工作。