SpringCloud LoadBalancerClient 负载均衡原理解析

目录
  • 深入解析 LoadBalancerClient 接口源码:
    • 1、LoadBalancerClient 源码解析:
    • 2、ILoadBalancer 源码解析:

LoadBalancerClient 是 SpringCloud 提供的一种负载均衡客户端,Ribbon 负载均衡组件内部也是集成了 LoadBalancerClient 来实现负载均衡。那么 LoadBalancerClient 内部到底是如何做到的呢?我们先大概讲一下 LoadBalancerClient 的实现原理,然后再深入源码中进行解析。

LoadBalancerClient 在初始化时会通过 Eureka Client 向 Eureka 服务端获取所有服务实例的注册信息并缓存在本地,并且每10秒向 EurekaClient 发送 “ping”,来判断服务的可用性。如果服务的可用性发生了改变或者服务数量和之前的不一致,则更新或者重新拉取。最后,在得到服务注册列表信息后,ILoadBalancer 根据 IRule 的策略进行负载均衡(默认策略为轮询)。

当使用 LoadBalancerClient 进行远程调用的负载均衡时,LoadBalancerClient 先通过目标服务名在本地服务注册清单中获取服务提供方的某一个实例,比如订单服务需要访问商品服务,商品服务有3个节点,LoadBalancerClient 会通过 choose() 方法获取到3个节点中的一个服务,拿到服务的信息之后取出服务IP信息,就可以得到完整的想要访问的IP地址和接口,最后通过 RestTempate 访问商品服务。

深入解析 LoadBalancerClient 接口源码:

1、LoadBalancerClient 源码解析:

LoadBalancerClient 是 Spring Cloud 提供的一个非常重要的接口,它继承自 ServiceInstanceChooser 接口,该接口的实现类是 RibbonLoadBalanceClient,它们之间的关系如下图所示:

(1)LoadBalancerClient 接口源码:

首先我们开始追踪 LoadBalancerClient 源码:

public interface LoadBalancerClient extends ServiceInstanceChooser
{
	<T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException;

    <T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException;

    URI reconstructURI(ServiceInstance instance, URI original);
}

可以发现 LoadBalancerClient 接口继承了 ServiceInstanceChooser 接口,主要的方法为2个 execute() 方法,均是用来执行请求的。还有个 reconstructURI() 是用来重构URL的。

(2)ServiceInstanceChooser 接口源码:

继续查看 LoadBalancerClient 继承的 ServiceInstanceChooser 接口源码,具体如下:

public interface ServiceInstanceChooser
{
	ServiceInstance choose(String serviceId);
}

ServiceInstanceChooser 接口中的主要方法为 choose(),该方法用于根据服务的名称 serviceId 来选择其中一个服务实例,即根据 serviceId 获取ServiceInstance。

(3)RibbonLoadBalanceClient 实现类源码:

接下来我们看看 LoadBalancerClient 的实现类 RibbonLoadBalanceClient,它用来执行最终的负载均衡请求。其中,RibbonLoadBalanceClient 的一个 choose() 方法用于选择具体的服务实例,其内部是通过 getServer() 方法交给 ILoadBalancer 完成的。我们先看下 RibbonLoadBalanceClient 里面几个重要实现方法的源码:

① 第一个:choose(),用来选择具体的服务实例。

@Override
	public ServiceInstance choose(String serviceId) {
		return choose(serviceId, null);
	}
	/**
	 * New: Select a server using a 'key'.
	 * @param serviceId of the service to choose an instance for
	 * @param hint to specify the service instance
	 * @return the selected {@link ServiceInstance}
	 */
	public ServiceInstance choose(String serviceId, Object hint) {
		Server server = getServer(getLoadBalancer(serviceId), hint);
		if (server == null) {
			return null;
		}
		return new RibbonServer(serviceId, server, isSecure(server, serviceId),
				serverIntrospector(serviceId).getMetadata(server));
	}

② 第二个:getServer(),获取实例。

protected Server getServer(ILoadBalancer loadBalancer) {
		return getServer(loadBalancer, null);
	}
	protected Server getServer(ILoadBalancer loadBalancer, Object hint) {
		if (loadBalancer == null) {
			return null;
		}
        // 最终通过 loadBalancer 去做服务实例的选择。
		// Use 'default' on a null hint, or just pass it on?
		return loadBalancer.chooseServer(hint != null ? hint : "default");
	}

可以看到最终通过 loadBalancer 去做服务实例的选择。那我们下面就看下 loadBalancer 是怎么怎么实现服务实例的选择的?

(4)BaseLoadBalancer 源码:

我们从上面能看到 ILoadBalancer 中的 chooseServer 方法里面默认值为 default,进入ILoadBalancer 实现类 BaseLoadBalancer 的 chooseServer() 看下:

我们的 key 的值为“default”,那么这个 key 代表的是什么意思呢?进去 rule 对象里面看下:

可以看到这个 rule 是 IRule 接口声明出来的,且默认定义的实现类是 RoundRobinRule(),也就是轮询策略。那我们接下来看下 IRule 接口:

(5)IRule 接口源码:

public interface IRule{
    /*
     * choose one alive server from lb.allServers or
     * lb.upServers according to key
     *
     * @return choosen Server object. NULL is returned if none
     *  server is available
     */
    public Server choose(Object key);

    public void setLoadBalancer(ILoadBalancer lb);

    public ILoadBalancer getLoadBalancer();
}

我们可以看到 IRule 接口定义了3个方法,choose() 是用来选择实例的,setLoadBalancer() 和 getLoadBalance() 用来设置和获取 ILoadBalancer 的。那么接下来 IRule 接口有多少个实现类:

(1)随机策略 RandomRule:随机数选择服务列表中的服务节点Server,如果当前节点不可用,则进入下一轮随机策略,直到选到可用服务节点为止

(2)轮询策略 RoundRobinRule:按照接收的请求顺序,逐一分配到不同的后端服务器

(3)重试策略 RetryRule:在选定的负载均衡策略机上重试机制,在一个配置时间段内当选择Server不成功,则一直尝试使用 subRule 的方式选择一个可用的server;

(4)可用过滤策略 PredicateBaseRule:过滤掉连接失败 和 高并发连接 的服务节点,然后从健康的服务节点中以线性轮询的方式选出一个节点返回

(5)响应时间权重策略 WeightedRespinseTimeRule:根据服务器的响应时间分配一个权重weight,响应时间越长,weight越小,被选中的可能性越低。主要通过后台线程定期地从 status 里面读取平均响应时间,为每个 server 计算一个 weight

(6)并发量最小可用策略 BestAvailableRule:选择一个并发量最小的服务节点 server。ServerStats 的 activeRequestCount 属性记录了 server 的并发量,轮询所有的server,选择其中 activeRequestCount 最小的那个server,就是并发量最小的服务节点。该策略的优点是可以充分考虑每台服务节点的负载,把请求打到负载压力最小的服务节点上。但是缺点是需要轮询所有的服务节点,如果集群数量太大,那么就会比较耗时。

(7)区域权重策略 ZoneAvoidanceRule:综合判断 server 所在区域的性能 和 server 的可用性,使用 ZoneAvoidancePredicate 和 AvailabilityPredicate 来判断是否选择某个server,前一个判断判定一个zone的运行性能是否可用,剔除不可用的zone(的所有server),AvailabilityPredicate 用于过滤掉连接数过多的Server。

同样的,如果我们也可以通过实现 IRule 接口来自定义一个负载均衡策略。

2、ILoadBalancer 源码解析:

ILoadBalancer 是一个接口,该接口定义了一系列实现负载均衡的方法,LoadBalancerClient 的实现类 RibbonLoadBalanceClient 也将负载均衡的具体实现交给了 ILoadBalancer 来处理,ILoadBalancer 通过配置 IRule、IPing 等,向 EurekaClient 获取注册列表信息,默认每10秒向 EurekaClient 发送一次 “ping”,进而检查是否需要更新服务的注册列表信息。最后,在得到服务注册列表信息后,ILoadBalancer 根据 IRule 的策略进行负载均衡。ILoadBalancer 接口的实现类结果如下图所示:

查看 BaseLoadBalancer 和 DynamicServerListLoadBalancer 源码,默认情况下实现了以下配置:

(1)IClientConfig clientConfig:用于配置负载均衡客户端,默认实现类是 DefaultClientConfigImpl。

(2)IRule rule:用于配置负载均衡的策略,默认使用的是 RoundRobinRule 轮询策略。

(3)IPing ping:用于检查当前服务是否有响应,从而判断当前服务是否可用,默认实现类是 DummyPing,该实现类的 isAlive() 方法返回值是 true,默认所有服务实例都是可用的。

(4)ServerList serverList:用于获取所有 Server 注册列表信息。通过跟踪源码会发现,ServerList 的实现类是 DiscoveryEnabledNIWSServerList,该类定义的 obtainServersViaDiscovery() 方法是根据 eurekaClientProvider.get() 方法获取 EurekaClient,再根据 EurekaClient 获取服务注册列表信息。EurekaClient 的实现类是DiscoveryClient,DiscoveryClient 具有服务注册、获取服务注册列表等功能。

(5)ServerListFilter filter:定义了根据配置过滤或者动态获取符合条件的服务列表,默认实现类是 ZonePreferenceServerListFilter,该策略能够优先过滤出与请求调用方处于同区域的服务实例。

到此这篇关于SpringCloud LoadBalancerClient 负载均衡原理的文章就介绍到这了,更多相关SpringCloud LoadBalancerClient 负载均衡内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • SpringCloud Ribbon 负载均衡的实现

    前言 Ribbon是一个客户端负载均衡器,它提供了对HTTP和TCP客户端的行为的大量控制.我们在上篇(猛戳:SpringCloud系列--Feign 服务调用)已经实现了多个服务之间的Feign调用,服务消费者调用服务提供者,本文记录Feign调用Ribbon负载均衡的服务提供者 GitHub地址:https://github.com/Netflix/ribbon 官方文档:https://cloud.spring.io/spring-cloud-static/spring-cloud-net

  • 详解SpringCloud的负载均衡

    目录 一.什么是负载均衡 二.负载均衡的简单分类 三.为什么需要做负载均衡 四.springCloud如何开启负载均衡 五.IRule 1.RandomRule:表示随机策略,它将从服务清单中随机选择一个服务: 2.ClientConfigEnabledRoundRobinRule:ClientConfigEnabledRoundRobinRule并没有实现什么特殊的处理逻辑,但是他的子类可以实现一些高级策略, 当一些本身的策略无法实现某些需求的时候,它也可以做为父类帮助实现某些策略,一般情况下

  • 深入理解Java SpringCloud Ribbon 负载均衡

    目录 前言 1.抛出问题 2.源码解析 2.1.LoadBalancerIntercepor 2.2.LoadBalancerClient 2.3.负载均衡策略IRule 2.4.总结 3.负载均衡策略 总结 前言 该技术博客是关于黑马视频教程的笔记总结! 服务消费者需要通过RestTemplate调用注册中心(Eureka)的服务提供者,但当同一服务名称的服务有多个的时候,我们的服务消费者应该调用哪一个服务呢?这时候就需要我们学习理解Ribbon负载均衡的实现原理. 当我们在RestTempl

  • SpringCloud LoadBalancerClient 负载均衡原理解析

    目录 深入解析 LoadBalancerClient 接口源码: 1.LoadBalancerClient 源码解析: 2.ILoadBalancer 源码解析: LoadBalancerClient 是 SpringCloud 提供的一种负载均衡客户端,Ribbon 负载均衡组件内部也是集成了 LoadBalancerClient 来实现负载均衡.那么 LoadBalancerClient 内部到底是如何做到的呢?我们先大概讲一下 LoadBalancerClient 的实现原理,然后再深入源

  • SpringCloud Ribbon负载均衡实例解析

    这篇文章主要介绍了SpringCloud Ribbon负载均衡实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 Spring Cloud集成了Ribbon,结合Eureka,可实现客户端的负载均衡. 下面实现一个例子,结构下图所示. 一.服务器端 1.创建项目 开发工具:IntelliJ IDEA 2019.2.3 IDEA中创建一个新的SpringBoot项目,名称为"cloud-server",SpringBoot版本选择2

  • SpringCloud 服务负载均衡和调用 Ribbon、OpenFeign的方法

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

  • SpringCloud 2020-Ribbon负载均衡服务调用的实现

    1.概述 官网:https://github.com/Netflix/ribbon/wiki/Getting-Started Ribbon目前也进入维护模式,未来替换方案: LB(负载均衡) 集中式LB 进程内LB Ribbon就是负载均衡+RestTemplate调用 2.Ribbon负载均衡演示 1.架构说明 总结:Ribbon其实就是一个软负载均衡的客户端组件,他可以和其他所需请求的客户端结合使用,和eureka结合只是其中的一个实例. 2. 3.二说RestTemplate的使用 官网

  • SpringCloud Eureka自我保护机制原理解析

    这篇文章主要介绍了SpringCloud Eureka自我保护机制原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 1. 自我保护机制演示 eureka在频繁修改微服务名称的时候,可以会出现如下现象: 2. 什么是自我保护模式? 默认情况下,如果EurekaServer在一定时间内没有接收到某个微服务实例的心跳,EurekaServer将会注销该实例(默认90秒).但是当网络分区故障发生时,微服务与EurekaServer之间无法正常通信

  • Pulsar负载均衡原理及优化方案详解

    目录 前言 Pulsar 负载均衡原理 ThresholdShedder 原理 问题原因 优化方案 总结 前言 前段时间我们在升级 Pulsar 版本的时候发现升级后最后一个节点始终没有流量. 虽然对业务使用没有任何影响,但负载不均会导致资源的浪费. 和同事沟通后得知之前的升级也会出现这样的情况,最终还是人工调用 Pulsar 的 admin API 完成的负载均衡. 这个问题我尝试在 Google 和 Pulsar 社区都没有找到类似的,不知道是大家都没碰到还是很少升级集群. 我之前所在的公司

  • 详解SpringCloud Ribbon 负载均衡通过服务器名无法连接的神坑

    一,问题 采取eureka集群.客户端通过Ribbon调用服务,Ribbon端报下列异常 java.net.UnknownHostException: SERVICE-HI java.lang.IllegalStateException: No instances available for SERVICE-HI java.lang.IllegalStateException: Request URI does not contain a valid hostname: http://SERVI

随机推荐