SpringCloud LoadBalancerClient 负载均衡原理

本文深入剖析了SpringCloud的LoadBalancerClient,它是实现服务间负载均衡的关键组件。LoadBalancerClient通过EurekaClient获取服务实例信息并缓存,每10秒检查服务可用性。在请求时,它根据ILoadBalancer和IRule策略(默认为轮询)选择服务实例。此外,还介绍了ILoadBalancer的实现,包括IRule的各种负载均衡策略,如轮询、随机、重试等。同时,ILoadBalancer还涉及IPing检查服务状态和ServerList获取服务列表。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        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,该策略能够优先过滤出与请求调用方处于同区域的服务实例。

<think>我们正在使用SpringCloud LoadBalancer作为负载均衡器,并且希望自定义负载均衡策略。根据引用[2],Spring CloudBalancer默认提供了轮询和随机两种策略。现在我们需要替换为自定义策略。步骤:1.创建自定义的负载均衡策略类,实现ReactorLoadBalancer接口(或使用SpringCloud LoadBalancer的API)。2.配置自定义的负载均衡策略,使其被Spring容器管理。3.通过配置指定使用自定义的负载均衡策略。具体实现如下:第一步:创建自定义负载均衡策略我们可以参考Spring CloudLoadBalancer中已有的实现(如RoundRobinLoadBalancer)来创建自己的策略。例如,我们创建一个简单的自定义负载均衡策略(假设我们实现一个总是选择第一个实例的策略,仅用于演示):```javaimportorg.springframework.cloud.client.ServiceInstance;import org.springframework.cloud.client.loadbalancer.Request;import org.springframework.cloud.client.loadbalancer.Response;import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer; importreactor.core.publisher.Mono; importjava.util.List;public classFirstInstanceLoadBalancer implementsReactorServiceInstanceLoadBalancer {privatefinal StringserviceId;privatefinal List<ServiceInstance>instances; //注意:实际中我们不会这样写,这里仅作示例//实际中,我们可能会使用一个ServiceInstanceSupplier来获取实例列表,这里简化publicFirstInstanceLoadBalancer(StringserviceId, List<ServiceInstance>instances){this.serviceId =serviceId;this.instances= instances;}@Overridepublic Mono<Response<ServiceInstance>>choose(Requestrequest){//总是返回第一个实例if (instances.isEmpty()){return Mono.empty();}returnMono.just(newResponse<ServiceInstance>() {@OverridepublicServiceInstancegetServer() {returninstances.get(0);}@Overridepublicboolean hasServer(){return true;}});} }```但是,上面的例子过于简单,实际中我们需要从服务发现中动态获取实例列表。因此,我们可以参考SpringCloud LoadBalancer的默认实现,使用一个ServiceInstanceListSupplier来获取实例列表。更合理的自定义负载均衡器实现(使用ServiceInstanceListSupplier): ```javaimport org.springframework.cloud.client.ServiceInstance;import org.springframework.cloud.client.loadbalancer.Request;import org.springframework.cloud.client.loadbalancer.Response;import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer; importorg.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier; importreactor.core.publisher.Mono; importjava.util.List;public classCustomLoadBalancerimplements ReactorServiceInstanceLoadBalancer{private finalServiceInstanceListSupplierserviceInstanceListSupplier;private finalString serviceId;publicCustomLoadBalancer(ServiceInstanceListSupplier serviceInstanceListSupplier,String serviceId){this.serviceInstanceListSupplier =serviceInstanceListSupplier;this.serviceId =serviceId;}@Overridepublic Mono<Response<ServiceInstance>>choose(Requestrequest){//获取可用的服务实例列表return serviceInstanceListSupplier.get().next().map(instances-> {//自定义选择逻辑:这里我们选择第一个实例if(instances.isEmpty()) {returnnull;}returnnew Response<ServiceInstance>(){@Overridepublic ServiceInstance getServer(){return instances.get(0);}@Overridepublicboolean hasServer(){return true;}};});} }```第二步:创建配置类,定义我们的自定义负载均衡器我们需要为每个服务创建一个负载均衡器实例。可以通过配置类来创建。 ```javaimport org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier; importorg.springframework.context.ConfigurableApplicationContext;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration; @Configurationpublic classCustomLoadBalancerConfiguration {@BeanpublicReactorLoadBalancer<ServiceInstance>customLoadBalancer(ConfigurableApplicationContext context,ServiceInstanceListSuppliersupplier){//这里我们使用服务名(可以通过配置获取,但这里为了简化,我们假设服务名是已知的,实际中可能需要动态获取)//注意:在实际中,我们可能需要为不同的服务配置不同的负载均衡器,这里我们使用同一个自定义类//但是,Spring CloudLoadBalancer允许我们为不同的服务配置不同的负载均衡配置类(通过@LoadBalancerClient注解指定)String serviceId= supplier.getServiceId();returnnew CustomLoadBalancer(supplier, serviceId);}} ```第三步:通过@LoadBalancerClient注解指定使用自定义负载均衡配置在启动类上或者配置类上,使用@LoadBalancerClient注解,指定哪个服务使用我们的自定义负载均衡配置。 ```javaimport org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient; importorg.springframework.context.annotation.Configuration; @Configuration@LoadBalancerClient(name= "your-service-name", configuration= CustomLoadBalancerConfiguration.class)public classMyLoadBalancerConfig{ }```注意:将"your-service-name"替换为你要应用自定义负载均衡策略的服务名。另外,也可以使用@LoadBalancerClients注解配置多个服务。这样,当通过OpenFeign调用"your-service-name"服务时,就会使用我们自定义的负载均衡策略。补充说明:在Spring CloudLoadBalancer中,我们还可以通过配置属性来切换默认的负载均衡策略(如轮询或随机),但自定义策略需要按照上述方式实现。参考引用[2]中提到的负载均衡器接口和已有实现,我们可以扩展自己的策略。注意:自定义负载均衡策略时,需要确保正确处理服务实例列表的获取和选择逻辑,并注意异常处理。以上就是在OpenFeign集成Spring CloudLoadBalancer时实现并替换为自定义负载均衡策略的方法。相关问题:1.如何为不同的微服务配置不同的负载均衡策略?2.Spring CloudLoadBalancer中如何实现健康检查过滤实例?3.自定义负载均衡策略时,如何获取请求的上下文信息(如请求头)来辅助决策?</think>### 在 OpenFeign 中集成自定义负载均衡策略 在 OpenFeign 集成 Spring Cloud LoadBalancer 时替换默认负载均衡算法,需通过以下步骤实现自定义策略: #### 1. **创建自定义负载均衡策略** 实现 `ReactorLoadBalancer` 接口,重写实例选择逻辑: ```java import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer; import reactor.core.publisher.Mono; public class CustomLoadBalancer implements ReactorServiceInstanceLoadBalancer { private final String serviceId; private final List<ServiceInstance> instances; public CustomLoadBalancer(String serviceId, List<ServiceInstance> instances) { this.serviceId = serviceId; this.instances = instances; } @Override public Mono<Response<ServiceInstance>> choose(Request request) { // 自定义选择逻辑(示例:权重优先算法) ServiceInstance selected = selectByWeight(instances); return Mono.just(new DefaultResponse(selected)); } private ServiceInstance selectByWeight(List<ServiceInstance> instances) { // 实现自定义权重算法 // 例如:根据实例元数据中的权重值选择 return instances.get(0); // 简化示例 } } ``` #### 2. **配置自定义负载均衡器** 通过配置类注册自定义策略: ```java import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration @LoadBalancerClient( name = "your-service", // 目标微服务名称 configuration = CustomLoadBalancerConfig.class ) public class CustomLoadBalancerConfig { @Bean public ReactorLoadBalancer<ServiceInstance> customLB( Environment environment, LoadBalancerClientFactory factory ) { String serviceId = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME); return new CustomLoadBalancer(serviceId, factory.getInstances(serviceId)); } } ``` #### 3. **禁用默认策略** 在 `application.yml` 中关闭默认负载均衡器: ```yaml spring: cloud: loadbalancer: ribbon: enabled: false # 禁用Ribbon(如果存在) configurations: custom # 使用自定义配置 ``` #### 关键原理说明 1. **动态代理机制** OpenFeign 通过动态代理生成客户端,`@FeignClient` 注解的接口会被拦截,实际调用由 `LoadBalancerFeignClient` 处理[^1]。 2. **负载均衡器选择** Spring Cloud LoadBalancer 通过 `ReactorLoadBalancer` 接口实现实例选择,默认提供轮询(`RoundRobinLoadBalancer`)和随机(`RandomLoadBalancer`)策略[^2]。 3. **配置生效流程** `@LoadBalancerClient` 注解优先于默认配置,Spring 容器会按名称将自定义策略注入到目标服务的调用链路中[^4]。 #### 验证自定义策略 在 Feign 客户端调用时添加日志: ```java @FeignClient(name = "your-service") public interface YourServiceClient { @GetMapping("/endpoint") String getData(); } ``` 观察控制台输出,确认请求是否按自定义逻辑路由到目标实例。 --- ### 相关问题 1. **如何为不同微服务配置不同的负载均衡策略?** 通过多个 `@LoadBalancerClient` 注解或 `@LoadBalancerClients` 注解,为每个服务指定独立的配置类。 2. **自定义策略如何实现健康检查过滤?** 在 `choose()` 方法中集成 `HealthCheckServiceInstanceListSupplier`,过滤掉不健康的实例[^2]。 3. **如何基于请求参数动态选择实例?** 通过 `Request` 对象获取请求上下文(如 Header 参数),在 `choose()` 方法中实现动态路由逻辑。 4. **自定义策略如何与 OpenFeign 重试机制协同工作?** 需配置 `Retryer` Bean 并设置重试次数,负载均衡失败时会触发重试[^3]。 5. **如何监控自定义策略的性能?** 集成 Micrometer 指标,在 `choose()` 方法中记录决策延迟和命中率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

张维鹏

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值