【SpringCloud】spring-cloud-loadbalancer 的实现 一
前言

 一图开场,SpringCloud 2020 版本开始,剔除了对所有除 eureka 以外 netflix 组件的支持,其中就包括我们最常用的 ribbon hystrix 等,同时 SpringCloud 也在 spring-cloud-commons 下提供了对 loadbalancer 的实现,可以拿来代替 ribbon。本文就分几个章节来简单讨论下 spring-cloud-loadbalancer 的实现(忽略 WebFlux 部分的实现)
第一章节从 spring-cloud-commons 部分看起,即一些 顶层抽象 核心注解 核心(默认)配置 等
spring-cloud-commons

 如图,spring-cloud-commons 包括了整个 SpringCloud 对各种 微服务化 组件的抽象,比如:
- doscovery:服务发现,比较经典的二方实现如- spring-cloud-netflix-eureka-client,依赖三方组件- netflix-eureka,也是- SpringCloud 2020版本后唯一支持的- netflix组件
- serviceregistry:服务注册,同上,- spring-cloud-commons针对- netflix-eureka的- server部分也提供了二方实现- spring-cloud-netflix-eureka-server
- loadbalancer:负载均衡,本文的重点,- spring-cloud-commons同时提供了一方实现- spring-cloud-loadbalancer,用来代替已经被废弃的- spring-cloud-netflix-ribbon
- 等等
@LoadBalanced
@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface LoadBalanced {
}
核心注解,被它修饰的 RestTemplate(AsyncRestTemplate)就具有了 负载均衡 的能力,此注解的精髓是其上标注的限定符注解 @Qualifier,使得后续我们可以通过 @LoadBalanced 来收集对应的 RestTemplate,细节见后文
ServiceInstanceChooser
public interface ServiceInstanceChooser {
	ServiceInstance choose(String serviceId);
	<T> ServiceInstance choose(String serviceId, Request<T> request);
}
顶层接口,方法不难理解,即对应服务实例的获取
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);
}
ServiceInstanceChooser 的子接口,区别于之后要介绍的 @LoadBalancerClient,跟 ServiceInstanceChooser 其实是一种组合关系:
- ServiceInstanceChooser选择对应实例
- LoadBalancerClient执行请求
- reconstructURI方法构造实例的真实- URI
LoadBalancerAutoConfiguration
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RestTemplate.class)
@ConditionalOnBean(LoadBalancerClient.class)
@EnableConfigurationProperties(LoadBalancerProperties.class)
public class LoadBalancerAutoConfiguration {
	/**
	 * 收集所有标注 @LoadBalanced 的 RestTemplate
	 */
	@LoadBalanced
	@Autowired(required = false)
	private List<RestTemplate> restTemplates = Collections.emptyList();
	/**
	 * 收集所有 RestTemplateCustomizer 对 RestTemplate
	 * 		进行定制
	 */
	@Bean
	public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(
			final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {
		return () -> restTemplateCustomizers.ifAvailable(customizers -> {
			for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
				for (RestTemplateCustomizer customizer : customizers) {
					customizer.customize(restTemplate);
				}
			}
		});
	}
	/**
	 * 非 retry 配置下
	 */
	@Configuration(proxyBeanMethods = false)
	@Conditional(RetryMissingOrDisabledCondition.class)
	static class LoadBalancerInterceptorConfig {
		@Bean
		public LoadBalancerInterceptor loadBalancerInterceptor(LoadBalancerClient loadBalancerClient,
				LoadBalancerRequestFactory requestFactory) {
			return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
		}
		/**
		 * 给 RestTemplate 注册拦截器
		 * @param loadBalancerInterceptor
		 * @return
		 */
		@Bean
		@ConditionalOnMissingBean
		public RestTemplateCustomizer restTemplateCustomizer(final LoadBalancerInterceptor loadBalancerInterceptor) {
			return restTemplate -> {
				List<ClientHttpRequestInterceptor> list = new ArrayList<>(restTemplate.getInterceptors());
				list.add(loadBalancerInterceptor);
				restTemplate.setInterceptors(list);
			};
		}
	}
	...
}
上述列出部分代码,核心自动装配类,该类由 spring-cloud-commons 提供(而非 spring-cloud-loadbalancer),因此提供了最 基本 的配置:
- 正如之前所说,此处收集容器中所有标注了 @LoadBalanced注解的RestTemplate(@Qualifer限定符的妙用)
- 对于上述 RestTemplate,使用(容器中)所有的RestTemplateCustomizer进行定制化处理
- 同时,针对 retry非 retry配置注册了一个RestTemplateCustomizer,代码中非 retry配置下的RestTemplateCustomizer是给所有RestTemplate插入了一个拦截器LoadBalancerInterceptor
- 同时,也注册了一个 LoadBalancerInterceptor,不难理解,RestTemplate负载均衡的能力由LoadBalancerInterceptor提供
LoadBalancerInterceptor
public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {
	// ...
	private LoadBalancerClient loadBalancer;
	@Override
	public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
			final ClientHttpRequestExecution execution) throws IOException {
		final URI originalUri = request.getURI();
		
		// serviceId
		String serviceName = originalUri.getHost();
		
		return this.loadBalancer.execute(serviceName, this.requestFactory.createRequest(request, body, execution));
	}
}
拦截器的核心方法自然就是 intercept,可以看到此处就是把执行委托给了 LoadBalancerClient loadBalancer,那么接下来就看 LoadBalancerClient 的具体规则了,也是需要实现和拓展的部分
总结

以上是本章节的流程图总结,至此,spring-cloud-commons 定义好了整个 loadbalance 的基调,那么各路神仙就可以在此基调下实现 SpringCloud 下的 loadbalance,同时 spring-cloud-commons 也给出了自己的 一方 实现: spring-cloud-loadbalancer
