【springcloud】Ribbon详解

简介: Spring Cloud Ribbon是一个基于HTTP和TCP的客户端负载均衡工具,它基于Netflix Ribbon实现。简单点说,其主要功能是提供客户端的软件负载均衡算法和服务调用。Ribbon客户端组件提供一系列完善的配置项如连接超时,重试等。简单的说,就是在配置文件中列出Load Balancer(简称LB)后面所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随机连接,权重等)去连接这些机器。

一、概述



Spring Cloud Ribbon是一个基于HTTP和TCP的客户端负载均衡工具,它基于Netflix Ribbon实现。简单点说,其主要功能是提供客户端的软件负载均衡算法和服务调用。Ribbon客户端组件提供一系列完善的配置项如连接超时,重试等。简单的说,就是在配置文件中列出Load Balancer(简称LB)后面所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随机连接,权重等)去连接这些机器。


  1. LB负载均衡(Load Balance)是什么?


简单的说就是将用户的请求平摊的分配到多个服务器上,从而达到系统的HA(高可用)。常见的负载均衡的软件有Nginx、LVS和硬件F5等。

  1. Ribbon本地负载均衡客户端与Nginx服务端负载均衡区别?
  • Nginx是服务器负载均衡,客户端所有请求都会交给nginx,然后由nginx实现转发请求,即负载均衡是由服务端实现的。即「集中式LB」,在服务端消费方和提供方之间使用独立的LB设施(可以是硬件,如F5,也可以是软件,如nginx),由该设施负责把访问请求通过某种策略转发至服务的提供方。
  • Ribbon本地负载均衡,在调用微服务接口时候,会在注册中心上获取注册信息服务列表之后缓存到本地JVM中,从而在本地实现RPC远程服务调用技术。即「进程内LB」,将LB逻辑集成到消费方,消费方从服务服务注册中心获知有哪些地址可用,然后自己再从这些地址中选择出一个合适的服务器。



二、基本使用



  1. 服务提供者只需要启动多个服务实例并注册到一个注册中心或多个相关联的服务注册中心。
  2. 服务消费者直接通过调用被@LoadBalanced注解修饰的Restemplate来实现面向服务的接口调用。


1. 使用@LoadBalanced


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 RemoteClientConfiguration {
      @Bean
      @LoadBalanced
      public RestTemplate restTemplate(){
          return new RestTemplate();
      }
  }


2. 调用服务提供者


公用java资源


public class ResultDto<T> {
      private Integer msgCode;
      private String msgTip;
      private T data;
      public Integer getMsgCode() {
          return msgCode;
      }
      public void setMsgCode(Integer msgCode) {
          this.msgCode = msgCode;
      }
      public String getMsgTip() {
          return msgTip;
      }
      public void setMsgTip(String msgTip) {
          this.msgTip = msgTip;
      }
      public T getData() {
          return data;
      }
      public void setData(T data) {
          this.data = data;
      }
      public static ResultDto error(Integer msgCode, String msgTip){
          ResultDto objectResultDto = new ResultDto();
          objectResultDto.setMsgCode(msgCode);
          objectResultDto.setMsgTip(msgTip);
          return objectResultDto;
      }
      public static <T> ResultDto success(Integer msgCode,String msgTip,T data){
          ResultDto objectResultDto = new ResultDto();
          objectResultDto.setMsgCode(msgCode);
          objectResultDto.setMsgTip(msgTip);
          objectResultDto.setData(data);
          return objectResultDto;
      }
  }


I、 GET请求


  1. 基本使用示例
//服务提供者名称
private static final String REMOTE_URL = "http://CLOUD-PAYMENT-SERVICE";
// getForEntity
@GetMapping("/payment/list")
public ResultDto listPayment(){
    ResponseEntity<ResultDto> forEntity = restTemplate.getForEntity(REMOTE_URL + "/payment/list", ResultDto.class);
    return forEntity.getBody();
}
// getForObject
@GetMapping("/payment/list")
public ResultDto listPayment(){
    return restTemplate.getForObject(REMOTE_URL + "/payment/list", ResultDto.class);
}


  1. getForEntity方法详解
/** 
  * 一、 getForEntity(String url, Class<T> responseType, Object... uriVariables)
  * url: 为请求地址
  * responseType: 响应结果类型
  * uriVariables: url中参数绑定,为变长参数(即数组),可以多个;它的顺序对应url中占位符定义的数字顺序。
  */
  @GetMapping("/payment/listEntity")
  public ResultDto listEntity(@RequestParam String uuid,@RequestParam String paymentType){
      //占位符中的数字对应变长参数的顺序
      ResponseEntity<ResultDto> forEntity = restTemplate.getForEntity(REMOTE_URL + "/payment/list?uuid={1}&paymentType={2}", ResultDto.class,uuid,paymentType);
      return forEntity.getBody();
  }
  /**
  * 二、 getForEntity(String url, Class<T> responseType, Map<String, ?> uriVariables)
  * url: 为请求地址
  * responseType: 响应结果类型
  * uriVariables: url中参数绑定,为map类型,key为url中占位符的值
  */
  @GetMapping("/payment/listEntity")
  public ResultDto listEntity(@RequestParam String uuid,@RequestParam String paymentType){
      Map<String,String> queryParams = new HashMap<>();
      queryParams.put("uuid",uuid);
      queryParams.put("paymentType",paymentType);
      //key为url中占位符的值
      ResponseEntity<ResultDto> forEntity = restTemplate.getForEntity(REMOTE_URL + "/payment/list?uuid={uuid}&paymentType={paymentType}", ResultDto.class,queryParams);
      return forEntity.getBody();
  }
  /**
  *三、getForEntity(URI url, Class<T> responseType)
  * url: 指定访问地址和参数绑定
  * responseType: 响应结果类型
  */
  @GetMapping("/payment/listEntity")
  public ResultDto listEntity(@RequestParam String uuid,@RequestParam String paymentType){
      UriComponents uriComponents = UriComponentsBuilder.fromUriString(REMOTE_URL + "/payment/list?uuid={uuid}&paymentType={paymentType}")
              .build()
              .expand(uuid)
              .expand(paymentType)
              .encode();
      URI uri = uriComponents.toUri();
      ResponseEntity<ResultDto> forEntity = restTemplate.getForEntity(uri, ResultDto.class);
      return forEntity.getBody();
  }


  1. getForObject详解
//参数及其示例同getForEntity,不在重复写
  1. getForObject(String url, Class<T> responseType, Object... uriVariables)
  2. getForObject(String url, Class<T> responseType, Map<String, ?> uriVariables)
  3. getForObject(URI url, Class<T> responseType)


II、POST请求


  1. 请求示例
// 1. postForObject
    @PostMapping("/save")
    public ResultDto save(@RequestBody Orders orders){
        Payment payment = new Payment();
        payment.setCreateTime(orders.getCreateTime());
        payment.setSerialPayment(orders.getSerialOrder());
        ResultDto resultDto = restTemplate.postForObject("http://localhost:8888" + "/payment/add", payment, ResultDto.class);
        return resultDto;
    }
    //2. postForEntity
    @PostMapping("/save")
    public ResultDto save(@RequestBody Orders orders){
        Payment payment = new Payment();
        payment.setCreateTime(orders.getCreateTime());
        payment.setSerialPayment(orders.getSerialOrder());
        ResultDto resultDto = restTemplate.postForObject("http://localhost:8888" + "/payment/add", payment, ResultDto.class);
        return resultDto;
    }
    //3. postForLocation,暂时没有想到应用场景,先只给一个使用方法
    URI uri = restTemplate.postForLocation(REMOTE_URL + "/save", payment);


III、PUT请求


1. void put(String var1, @Nullable Object var2, Object... var3)
2. void put(String var1, @Nullable Object var2, Map<String, ?> var3)
3. void put(URI var1, @Nullable Object var2)
//其用法和postForObject基本类似,不再赘述


XI、DELETE请求


1. void delete(String var1, Object... var2);
2. void delete(String var1, Map<String, ?> var2);
3. void delete(URI var1);
//其用法和postForObject基本类似,不再赘述


三、Ribbon工作流程



  1. 选择EurekaServer(注册中心),优先选择同一个区域内负载最少的EurekaServer
  2. 根据用户指定的策略,从注册中心获取的服务列表中选择一个服务地址并执行


其中Ribbon提供了多种策略,如下:


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


四、Ribbon更换默认策略



  1. java bean
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
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 RemoteClientConfiguration {
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
    //更换策略为随机
    @Bean
    public IRule myRule(){
        return new RandomRule();
    }
}


  1. application.yml
#设置负载均衡策略 cloud-payment-service 为调用的服务的名称  value为策略全限定名
cloud-payment-service:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule


五、自定义负载均衡策略



注意:官方文档明确指出,自定义配置类不能放在@ComponentScan所扫描的当前包下及其子包下,否则我们自定义的配置类就会被所有的Ribbon客户端所共享,达步到特殊化定制的目的。


  1. 在Order(ComponentScan能扫描到的最跟目录)上新建同级目录myrule


  1. 在myrule包下新建MySelfRule
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MySelfRule {
    @Bean
    public IRule myRule(){
        //这里可以自定义规则,为了演示方便,直接使用了随机策略
        return new RandomRule(); //随机
    }
}


  1. 在主启动类上添加@RibbonClient
@EnableSwagger2 //swagger
@EnableEurekaClient //eureka
@EnableDiscoveryClient //服务发现
@SpringBootApplication //springboot启动注解
@RibbonClient(name = "cloud-payment-service",configuration = MySelfRule.class) //name为要使用的服务名字,configuration指向自己定义的配置
@MapperScan("com.yuyue.online.springcloud.order.mapper")
public class OrderApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderApplication.class,args);
    }
}
相关实践学习
部署高可用架构
本场景主要介绍如何使用云服务器ECS、负载均衡SLB、云数据库RDS和数据传输服务产品来部署多可用区高可用架构。
负载均衡入门与产品使用指南
负载均衡(Server Load Balancer)是对多台云服务器进行流量分发的负载均衡服务,可以通过流量分发扩展应用系统对外的服务能力,通过消除单点故障提升应用系统的可用性。 本课程主要介绍负载均衡的相关技术以及阿里云负载均衡产品的使用方法。
目录
相关文章
|
4天前
|
JSON 负载均衡 Java
Spring Cloud Ribbon:负载均衡的服务调用
Spring Cloud Ribbon:负载均衡的服务调用
68 0
|
4天前
|
缓存 负载均衡 算法
【微服务 SpringCloud】实用篇 · Ribbon负载均衡
【微服务 SpringCloud】实用篇 · Ribbon负载均衡
16 0
|
4天前
|
负载均衡 算法
SpringCloud&Ribbon负载均衡原理与实践
SpringCloud&Ribbon负载均衡原理与实践
21 3
|
4天前
|
负载均衡 算法 Java
第五章 Spring Cloud Netflix 之 Ribbon
第五章 Spring Cloud Netflix 之 Ribbon
21 0
|
4天前
|
负载均衡 算法 Java
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(四)Ribbon的使用
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(四)Ribbon的使用
29 0
|
4天前
|
负载均衡
【二十】搭建SpringCloud项目四(Ribbon)
【二十】搭建SpringCloud项目四(Ribbon)
26 0
|
4天前
|
存储 负载均衡 Java
【Spring底层原理高级进阶】微服务 Spring Cloud 的注册发现机制:Eureka 的架构设计、服务注册与发现的实现原理,深入掌握 Ribbon 和 Feign 的用法 ️
【Spring底层原理高级进阶】微服务 Spring Cloud 的注册发现机制:Eureka 的架构设计、服务注册与发现的实现原理,深入掌握 Ribbon 和 Feign 的用法 ️
|
4天前
|
负载均衡 算法 Java
SpringCloud负载均衡源码解析 | 带你从表层一步步剖析Ribbon组件如何实现负载均衡功能
SpringCloud负载均衡源码解析 | 带你从表层一步步剖析Ribbon组件如何实现负载均衡功能
|
4天前
|
负载均衡 Java 应用服务中间件
springcloud3-服务到服务调用ribbon及openfeign
springcloud3-服务到服务调用ribbon及openfeign
47 0
|
4天前
|
存储 负载均衡 算法
spring cloud 之 Ribbon
spring cloud 之 Ribbon
52 0