springcloud-springcloudnetflix之ribbon(代码片段)

MinggeQingchun MinggeQingchun     2022-12-05     170

关键词:

阅读本文前可先参考 

SpringCloud - Spring Cloud根/父项目,开发准备(二)_MinggeQingchun的博客-CSDN博客

Spring Cloud Ribbon 是一套基于 Netflix Ribbon 实现的客户端负载均衡和服务调用工具

Ribbon 是 Netflix 公司发布的开源组件,其主要功能是提供客户端的负载均衡算法和服务调用。

Spring Cloud 将其与 Netflix 中的其他开源服务组件(例如 Eureka、Feign 以及 Hystrix 等)一起整合进 Spring Cloud Netflix 模块中,整合后全称为 Spring Cloud Netflix Ribbon

Ribbon 是 Spring Cloud Netflix 模块的子模块,它是 Spring Cloud 对 Netflix Ribbon 的二次封装。通过它,我们可以将面向服务的 REST 模板(RestTemplate)请求转换为客户端负载均衡的服务调用。

Ribbon 是 Spring Cloud 体系中最核心、最重要的组件之一。它虽然只是一个工具类型的框架,并不像 Eureka Server(服务注册中心)那样需要独立部署,但它几乎存在于每一个使用 Spring Cloud 构建的微服务中。

Spring Cloud 微服务之间的调用,API 网关的请求转发等内容,实际上都是通过 Spring Cloud Ribbon 来实现的,它会从eureka中获取一个可用的服务端清单,通过心跳检测来剔除故障的服务端节点以保证清单中都是可以正常访问的服务端节点。

当客户端发送请求,则ribbon负载均衡器按某种算法(比如轮询、权重、 最小连接数等)从维护的可用服务端清单中取出一台服务端的地址,然后进行请求

Ribbon非常简单,可以说就是一个jar包,这个jar包实现了负载均衡算法。

一、RestTemplate

Spring 官网对它的介绍如下

RestTemplate: The original Spring REST client with a synchronous, template method API.

RestTemplate 是一个同步的 Rest API 客户端

RestTemplate 是从 Spring3.0 开始支持的一个 HTTP 请求工具,它提供了常见的REST请求方案的模版,例如 GET 请求、POST 请求、PUT 请求、DELETE 请求以及一些通用的请求执行方法 exchange 以及 execute。

RestTemplate 继承自 InterceptingHttpAccessor 并且实现了 RestOperations 接口,其中 RestOperations 接口定义了基本的 RESTful 操作,这些操作在 RestTemplate 中都得到了实现

RestTemplate 提供高度封装的接口,可以让我们非常方便地进行 Rest API 调用,分别用三种 Java 最常用 Http 连接的库来分别实现这套接口:

  • JDK 自带的 HttpURLConnection
  • Apache 的 HttpClient
  • OKHttp3

RestTemplate 的方法

二、负载均衡(Load Balance) 

负载均衡(Load Balance) ,简单点说就是将用户的请求平摊分配到多个服务器上运行,以达到扩展服务器带宽、增强数据处理能力、增加吞吐量、提高网络的可用性和灵活性的目的

负载均和分为硬件负载均衡和软件负载均衡:

硬件负载均衡:比如 F5、深信服、Array 等;

软件负载均衡:比如 Nginx、LVS、HAProxy 等;(是一个服务器实现的)

一般来说,使用硬件成本比较高,因此公司都是使用软件负载均衡,软件负载均衡又分为两种

  • 服务端负载均衡
  • 客户端负载均衡

1、服务端负载均衡

服务端负载均衡是在客户端和服务端之间建立一个独立的负载均衡服务器,该服务器既可以是硬件设备(例如 F5),也可以是软件(例如 Nginx)。这个负载均衡服务器维护了一份可用服务端清单,然后通过心跳机制来删除故障的服务端节点,以保证清单中的所有服务节点都是可以正常访问的。
当客户端发送请求时,该请求不会直接发送到服务端进行处理,而是全部交给负载均衡服务器,由负载均衡服务器按照某种算法(例如轮询、随机等),从其维护的可用服务清单中选择一个服务端,然后进行转发。

2、客户端负载均衡

客户端负载均衡是将负载均衡逻辑以代码的形式封装到客户端上,即负载均衡器位于客户端。客户端通过服务注册中心(例如 Eureka Server)获取到一份服务端提供的可用服务清单。有了服务清单后,负载均衡器会在客户端发送请求前通过负载均衡算法选择一个服务端实例再进行访问,以达到负载均衡的目的 

Ribbon 就是一个基于 HTTP 和 TCP 的客户端负载均衡器,当我们将 Ribbon 和 Eureka 一起使用时,Ribbon 会从 Eureka Server(服务注册中心)中获取服务端列表,然后通过负载均衡策略将请求分摊给多个服务提供者,从而达到负载均衡的目的

3、使用Ribbon实现服务调用

1、首先加入ribbon的依赖,eureka已经依赖了ribbon(项目 springcloud-3-service-eureka),这里不需要再引用ribbon的依赖

2、要使用ribbon,需要一个注解 @LoadBalanced 注解 (项目 springcloud-3-service-eureka-consumer)

@Configuration
public class RestConfig 

    //使用Ribbon实现负载均衡的调用
    @LoadBalanced
    @Bean
    public RestTemplate restTemplate () 
        return new RestTemplate();
    

在RestTemplate上面加入@LoadBalanced注解,这样就可以实现RestTemplate 在调用时自动负载均衡

三、Ribbon 实现负载均衡策略 IRule、ILoadBalancer  接口

Ribbon 是一个客户端的负载均衡器,它可以与 Eureka 配合使用轻松地实现客户端的负载均衡。Ribbon 会先从 Eureka Server(服务注册中心)去获取服务端列表,然后通过负载均衡策略将请求分摊给多个服务端,从而达到负载均衡的目的

Spring Cloud Ribbon 提供了一个 IRule 接口,该接口主要用来定义负载均衡策略

在jar包:com.netflix.ribbon:ribbon-loadbalancer 

这个层级图

1、我们可以 选中 接口或 类,右击 Diagrams ----> Show Diagram...

2、选中类 或 接口,右击,选中需要展示的 实现类等

3、全部选中,点击 Enter

最终 IRule接口 层级关系如下

ILoadBalancer 接口层级关系如下

负载均衡的入口:ILoadBalancer 接口

通过断点调试,选中 ILoadBalancer 查看其实现类,在实现类中分别断点,最终进入

ZoneAwareLoadBalancer 类,因此可判断出 默认负载均衡策略是 ZoneAvoidanceRule

IRule有 7 个默认实现类,每一个实现类都是一种负载均衡策略

负载均衡实现

策略

RandomRule

随机

RoundRobinRule

轮询

AvailabilityFilteringRule

先过滤掉由于多次访问故障的服务,以及并发连接数超过阈值的服务,然后对剩下的服 务按照轮询策略进行访问;

WeightedResponseTimeRule

根据平均响应时间计算所有服务的权重,响应时间越快服务权重就越大被选中的概率即越高,如果服务刚启动时统计信息不足,则使用RoundRobinRule策略,待统计信息足够会切换到该WeightedResponseTimeRule策略;

RetryRule

先按照RoundRobinRule策略分发,如果分发到的服务不能访问,则在指定时间内进行重 试,然后分发其他可用的服务;

BestAvailableRule

先过滤掉由于多次访问故障的服务,然后选 择一个并发量最小的服务;

ZoneAvoidanceRule (默认)

综合判断服务节点所在区域的性能和服务节 点的可用性,来决定选择哪个服务;

我们这里启动了eureka集群(3个eureka,application-eureka8761.properties,application-eureka8761.properties,application-eureka8761.properties)

服务提供者集群(2个服务提供者,springcloud-4-service-eureka-ribbon-provider9001,springcloud-4-service-eureka-ribbon-provider9002)

一个服务调用者(springcloud-4-service-eureka-ribbon-consumer)

1、切换负载均衡策略

在 项目 springcloud-4-service-eureka-ribbon-consumer 的配置类

@Bean
    public IRule myRule()
        //采用轮询策略
        return new RoundRobinRule();
    
@Configuration
public class RestConfig 

    //使用Ribbon实现负载均衡的调用 (spring cloud -> 封装ribbon + eureka + restTemplate)
    @LoadBalanced
    @Bean
    public RestTemplate restTemplate () 
        return new RestTemplate();
    

    @Bean
    public IRule myRule()
        //采用轮询策略
        return new RoundRobinRule();
    

2、自定义 负载均衡策略

在 项目 springcloud-4-service-eureka-ribbon-consumer 创建一个 自定义策略类

import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;

import java.util.List;
import java.util.Random;

/**
 * 自定义负载均衡算法
 */
public class MyRule extends AbstractLoadBalancerRule 

    @Override
    public void initWithNiwsConfig(IClientConfig clientConfig) 
    

    @Override
    public Server choose(Object o) 
        System.out.println("自定义负载均衡策略..........");
        //自己实现服务的选择
        ILoadBalancer lb = getLoadBalancer();
        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) 
                /*
                 * No servers. End regardless of pass, because subsequent passes
                 * only get more restrictive.
                 */
                return null;
            

            int index =  new Random().nextInt(serverCount);
            server = upList.get(index);

            if (server == null) 
                /*
                 * The only time this should happen is if the server list were
                 * somehow trimmed. This is a transient condition. Retry after
                 * yielding.
                 */
                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;
    

在 项目 springcloud-4-service-eureka-ribbon-consumer 的配置类调用即可

@Configuration
public class RestConfig 

    //使用Ribbon实现负载均衡的调用 (spring cloud -> 封装ribbon + eureka + restTemplate)
    @LoadBalanced
    @Bean
    public RestTemplate restTemplate () 
        return new RestTemplate();
    

    @Bean
    public IRule myRule()
        //采用自定义负载均衡策略
        return new MyRule();
        
//        //采用轮询策略
//        return new RoundRobinRule();