自定义Spring Cloud LoadBalancer实践

  1. Spring Cloud负载均衡概述
  2. 自定义Spring Cloud LoadBalancer默认实现

Spring Cloud负载均衡概述

在不同的Spring Cloud版本中,采用了不同的负载均衡组件。
具体来说,在Spring Cloud 2020.0版本之前,默认负载均衡器为Netflix推出的Ribbon,自Spring Cloud 2020.0版本起,Ribbon已经被标记为过时,官方推荐使用Spring Cloud LoadBalancer

在本文中阐述的是如何自定义Spring Cloud LoadBalancer默认实现,对应的框架版本信息如下:

  • Spring Cloud版本:2021.0.5
  • Spring Cloud Aliaba版本:2021.0.5.0
  • Spring Boot版本:2.6.13

自定义Spring Cloud LoadBalancer默认实现

Spring Cloud LoadBalancer组件中,默认的负载均衡器配置是通过org.springframework.cloud.loadbalancer.annotation.LoadBalancerClientConfiguration.reactorServiceInstanceLoadBalancer()方法实现的。

@Configuration(proxyBeanMethods = false)
@ConditionalOnDiscoveryEnabled
public class LoadBalancerClientConfiguration {
    @Bean
	@ConditionalOnMissingBean
	public ReactorLoadBalancer<ServiceInstance> reactorServiceInstanceLoadBalancer(Environment environment,
			LoadBalancerClientFactory loadBalancerClientFactory) {
		String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
        // 在Spring Cloud LoadBalancer组件中,默认的负载均衡器是RoundRobinLoadBalancer
		return new RoundRobinLoadBalancer(
				loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
	}
}

从源码来看,Spring Cloud LoadBalancer组件的默认负载均衡器为org.springframework.cloud.loadbalancer.core.RoundRobinLoadBalancer,它通过轮训方式为服务消费者路由服务提供者。

值得注意的是,org.springframework.cloud.loadbalancer.annotation.LoadBalancerClientConfiguration.reactorServiceInstanceLoadBalancer()方法只会在首次调用RPC服务提供者时才会被调用,不同的RPC服务提供者首次访问都会调用,可以根据该机制为不通的服务提供者定制不同的负载均衡策略。

如果需要自定义配置负载均衡器,需要通过注解@LoadBalancerClientsdefaultConfiguration属性指定才会生效。

自定义负载均衡路由器:

/**
 * 基于XXL-JOB定时任务执行器的分片信息进行下游服务的路由
 */
public class ShardBasedLoadBalancer implements ReactorServiceInstanceLoadBalancer {
    private static final Logger logger = LoggerFactory.getLogger(ShardBasedLoadBalancer.class);
    private final String serviceId;
    private final ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplier;
    private final Random random = new Random();

    /**
     * 构造函数
     * @param serviceInstanceListSupplier 实例列表供应商
     * @param serviceId 目标服务名
     */
    public ShardBasedLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplier, String serviceId) {
        this.serviceInstanceListSupplier = serviceInstanceListSupplier;
        this.serviceId = serviceId;
    }

    /**
     * 重写选择服务提供者方法
     * @param request 请求对象
     * @return {@link Response}
     */
    @Override
    public Mono<Response<ServiceInstance>> choose(Request request) {
        ServiceInstanceListSupplier supplier = serviceInstanceListSupplier.getIfAvailable(NoopServiceInstanceListSupplier::new);
        return supplier.get().next().map(instances -> {
            if (instances.isEmpty()) {
                if (logger.isWarnEnabled()) {
                    logger.warn("No servers available for service:{}", serviceId);
                }
                return new EmptyResponse();
            }

            // 根据定时任务的路由策略选择下游服务提供者
            int shardTotal = 0;
            int shardIndex = 0;
            ServiceInstance targetInstance;
            if ((shardIndex == 0 && shardTotal == 0) || (shardTotal > instances.size())) {
                // 不是分片广播策略,或者在分片广播策略时执行器数量大于下游服务提供者数量
                targetInstance = instances.get(random.nextInt(instances.size()));
            } else {
                targetInstance = instances.get(shardIndex);
            }
            return new DefaultResponse(targetInstance);
        });
    }
}

在配置类中加载自定义负载均衡器(注意:不要在在配置类上使用@Configuration注解):

public class LoadBalancerConfig {
    @Bean
    @ConditionalOnMissingBean
    public ReactorLoadBalancer<ServiceInstance> reactorServiceInstanceLoadBalancer(
            Environment environment,
            LoadBalancerClientFactory loadBalancerClientFactory) {
        // 获取要调用的目标服务名
        String serviceName = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
        return new ShardBasedLoadBalancer(loadBalancerClientFactory.getLazyProvider(serviceName, ServiceInstanceListSupplier.class), serviceName);
    }
}

通过@LoadBalancerClients注解指定负载均衡配置类:

@EnableDiscoveryClient   // 启用服务注册与发现
@EnableFeignClients // 启用Feign
@SpringBootApplication
@LoadBalancerClients(defaultConfiguration = LoadBalancerConfig.class) // 指定负载均衡配置类
public class SampleAppApplication {
    public static void main(String[] args) {
        SpringApplication.run(SampleAppApplication.class, args);
    }
}

【参考】
Spring Cloud Alibaba全家桶(三)——微服务负载均衡器Ribbon与LoadBalancer
LoadBalancer和Ribbon的区别是什么?
Spring Cloud:负载均衡 - Spring Cloud Loadbalancer原理


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达,在下面评论区告诉我^_^^_^