Ribbon负载均衡实现

Ribbon

Spring Cloud Ribbon 是基于Netflix Ribbon 实现的一套客户端负载均衡的工具,主要功能是提供客户端的软件负载均衡算法,将 Netflix 的中间层服务连接在一起。Ribbon 的客户端组件提供一系列完整的配置项,如:连接超时、重试等。

在配置文件中列出 LoadBalancer (简称LB:负载均衡) 后面所有的机器,Ribbon 会自动的帮助你基于某种规则 (如简单轮询,随机连接等等) 去连接这些机器,也可以使用 Ribbon 实现自定义的负载均衡算法。

负载均衡:简单的说就是将用户的请求平摊的分配到多个服务上,从而达到系统的HA (高可用)。
负载均衡简单分类:

  • 集中式LB:即在服务的提供方和消费方之间使用独立的LB设施,如Nginx,由该设施负责把访问请求通过某种策略转发至服务的提供方。
  • 进程式LB:将LB逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选出一个合适的服务器。

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

Ribbon的角色

要实现Ribbon负载均衡至少需要一个服务注册中心、至少两个服务提供者、一个服务消费者。

负载均衡流程:

  1. 服务提供者在服务注册中心注册

  2. 服务消费者向服务注册中心发出请求

  3. 服务注册中心将服务提供者地址提供给消费者

  4. 消费者根据负载均衡策略挑选服务

实现

前置条件:学习Eureka搭建服务,学习restful发送请求

服务注册中心搭建

使用Eureka章节搭建的Eureka服务端

服务提供者搭建

负载均衡至少需要两个服务提供者

步骤:

  1. 新建两个maven子模块:springcloud-eureka-client-8001、springcloud-eureka-client-8002
  2. 添加依赖
  3. 编写application.yaml
  4. 编写controller
  5. 编写启动类

实现

  1. 新建maven子模块:springcloud-eureka-client-8001、springcloud-eureka-client-8002

  2. 添加依赖:两个服务提供者依赖相同

  3. 编写application.yaml

    1. springcloud-eureka-client-8001
    spring:
      application:
        name: springcloud-eureka-client #服务注册中心的服务名,提供相同服务的提供者需要同名
    server:
      port: 8001
    eureka:
      instance:
        instance-id: client8001
        prefer-ip-address: true #访问路径可以显示ip
      client:
        service-url:
          defaultZone: http://127.0.0.1:2001/eureka #表示eureka服务器的部署位置
        register-with-eureka: true
        fetch-registry: true
    
    1. springcloud-eureka-client-8002
    spring:
      application:
        name: springcloud-eureka-client #服务注册中心的服务名,提供相同服务的提供者需要同名
    server:
      port: 8002
    eureka:
      instance:
        instance-id: client8002
        prefer-ip-address: true #访问路径可以显示ip
      client:
        service-url:
          defaultZone: http://127.0.0.1:2001/eureka #表示eureka服务器的部署位置
        register-with-eureka: true
        fetch-registry: true
    
  4. 编写controller

    1. springcloud-eureka-client-8001

      @ResponseBody
      @Controller
      public class TestController {
          @GetMapping(value = "/payment/get")
          public String getPaymentById()
          {
              return "get8001";
          }
      }
      
    2. springcloud-eureka-client-8002

      @ResponseBody
      @Controller
      public class TestController {
          @GetMapping(value = "/payment/get")
          public String getPaymentById()
          {
              return "get8002";
          }
      }
      
  5. 编写启动类:两个服务提供者启动类相同

    @EnableDiscoveryClient
    @EnableEurekaClient
    @SpringBootApplication
    public class SpringCloudEurekaClientApplication {
        public static void main(String[] args) {
            SpringApplication.run(SpringCloudEurekaClientApplication.class,args);
        }
    }
    

服务消费者搭建

步骤:

  1. 新建maven子模块:springcloud-eureka-consumer
  2. 添加依赖
  3. 编写application.yaml
  4. 编写配置类
  5. 编写自定义配置类
  6. 编写controller
  7. 编写启动类
  8. 测试

实现

  1. 新建maven子模块:springcloud-eureka-consumer

  2. 添加依赖

<dependency>
    <groupId>org.springframework.cloudgroupId>
    <artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
    <version>1.4.6.RELEASEversion>
dependency>
  1. 编写application.yaml
spring:
  application:
    name: springcloud-eureka-consumer
server:
  port: 80
eureka:
  instance:
    instance-id: consumer80
    prefer-ip-address: true #访问路径可以显示ip
  client:
    service-url:
      defaultZone: http://127.0.0.1:2001/eureka #表示eureka服务器的部署位置
    register-with-eureka: true
    fetch-registry: true
  1. 编写配置类。负载均衡在restTemplate中配置,此方法可以放在自定义配置类里,此处为区分业务放在不同类。
@Configuration
public class ApplicationContextConfig {
    @Bean
    @LoadBalanced//使用@LoadBalanced注解赋予RestTemplate负载均衡的能力
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}
  1. 编写自定义配置类,配置负载均衡策略。此类不可放在@componentScan扫描的包及其子包下,即与springboot启动类不同包
@Configuration
public class MySelfRule {
    @Bean //lRule:根据特定算法中从服务列表中选取一个要访问的服务
    public IRule myRule(){
        return new RandomRule();//随机访问
    }
}

自定义配置类放置如图
Ribbon负载均衡实现_第1张图片

  1. 编写controller
@ResponseBody
@Controller
public class TestController {
	//载均衡服务提供者的服务名
    public static final String PAYMENT_URL="http://SPRINGCLOUD-EUREKA-CLIENT";

    @Autowired
    private RestTemplate restTemplate;
	
    @GetMapping("/consumer/payment/get")
    public String getPayment(){
        //RestTemplate根据负载均衡策略向服务提供者发送请求
        return restTemplate.getForObject(PAYMENT_URL+"/payment/get/",String.class);
    }
}
  1. 编写启动类
@EnableEurekaClient
@SpringBootApplication
/**
name:负载均衡服务提供者的服务名
configuration:负载均衡策略类的class
*/
@RibbonClient(name = "SPRINGCLOUD-EUREKA-CLIENT", configuration = MySelfRule.class)
public class SpringCloudEurekaClientApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringCloudEurekaClientApplication.class,args);
    }
}
  1. 测试
    1. 启动服务:springcloud-eureka-server、springcloud-eureka-client-8001、springcloud-eureka-client-8002、springcloud-eureka-consumer
    2. 访问:http://localhost/consumer/payment/get
    3. 返回结果在get8001与get8002之内随机算选择

自带负载均衡策略

  1. RoundRobinRule:轮询。在存活服务中随机选择,默认策略。
  2. RandomRule:随机访问。在存活服务中随机选择。
  3. RetryRule:轮询重试。先按照RoundRobinRule的策略获取服务,如果获取服务失败则在指定时间内会进行重试,默认重试间隔500毫秒。
  4. WeightedResponseTimeRule:响应速度决定权重。是对RoundRobinRule的扩展,响应速度越快的实例选择权重越大,权重越大越容易被选择。
  5. BestAvailableRule:最优可用。优先选择并发连接数最少的服务,有固定的默认最小连接数,当所有服务的连接数都大于默认最小连接数,使用RoundRobinRule策略。
  6. AvailabilityFilteringRule:可用性过滤规则。先过滤掉故障实例,再选择并发连接数较小的实例。
  7. ZoneAvoidanceRule:区域内可用性能最优。首先判断一个zone的运行性能是否可用,剔除不可用的区域zone的所有server,然后再利用AvailabilityPredicate过滤并发连接过多的服务。

自定义负载均衡策略

自定义算法类必须继承 AbstractLoadBalancerRule类

  1. 创建自定义配置类
  2. 修改启动类
  3. 测试

实现

  1. 新建自定义配置类CustomerRule,并继承AbstractLoadBalancerRule类
@Configuration
public class CustomerRule extends AbstractLoadBalancerRule{
    //自定义逻辑:调用同一个服务5次后,再轮询调用下一个服务
    /*
     total = 0 // 当total==5,调用下一个服务
     currentIndex = 0 // 当前对外提供服务的服务器下标
     */

    private int total = 0;  // 总共被调用的次数,目前要求每台被调用5次
    private int currentIndex = 0;  // 当前提供服务的机器号

    public Server choose(ILoadBalancer lb, Object key) {
        if (lb == null) {
            return null;
        }
        Server server = null;

        while (server == null) {
            if (Thread.interrupted()) {
                return null;
            }
            List<Server> upList = lb.getReachableServers(); //当前存活的服务
            List<Server> allList = lb.getAllServers();  //获取全部的服务

            int serverCount = allList.size();
            if (serverCount == 0) {
                return null;
            }

            if(total < 5)
            {
                server = upList.get(currentIndex);
                total++;
            }else {
                total = 0;
                currentIndex++;
                if(currentIndex >= upList.size())
                {
                    currentIndex = 0;
                }
            }

            if (server == null) {
                Thread.yield();
                continue;
            }

            if (server.isAlive()) {
                return (server);
            }

            // Shouldn't actually happen.. but must be transient or a bug.
            server = null;
            Thread.yield();
        }
        return server;
    }

    @Override
    public Server choose(Object key) {
        return choose(getLoadBalancer(), key);
    }

    @Override
    public void initWithNiwsConfig(IClientConfig clientConfig) {
    }
}
  1. 修改启动类
@EnableEurekaClient
@SpringBootApplication
//修改configuration,指定为自定义配置类
@RibbonClient(name = "SPRINGCLOUD-EUREKA-CLIENT", configuration = CustomerRule.class)
public class SpringCloudEurekaClientApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringCloudEurekaClientApplication.class,args);
    }
}
  1. 测试
    1. 启动服务:springcloud-eureka-server、springcloud-eureka-client-8001、springcloud-eureka-client-8002、springcloud-eureka-consumer
    2. 访问:http://localhost/consumer/payment/get
    3. 返回结果在get8001与get8002之间切换,一个结果加载5次后切换到另一个

你可能感兴趣的:(ribbon,spring,cloud,eureka)