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

  • Post author:
  • Post category:其他

前言

sc-2020
一图开场,SpringCloud 2020 版本开始,剔除了对所有除 eureka 以外 netflix 组件的支持,其中就包括我们最常用的 ribbon hystrix 等,同时 SpringCloud 也在 spring-cloud-commons 下提供了对 loadbalancer 的实现,可以拿来代替 ribbon。本文就分几个章节来简单讨论下 spring-cloud-loadbalancer 的实现(忽略 WebFlux 部分的实现)

第一章节从 spring-cloud-commons 部分看起,即一些 顶层抽象 核心注解 核心(默认)配置

spring-cloud-commons

sc-commons
如图,spring-cloud-commons 包括了整个 SpringCloud 对各种 微服务化 组件的抽象,比如:

  • doscovery:服务发现,比较经典的二方实现如 spring-cloud-netflix-eureka-client,依赖三方组件 netflix-eureka,也是 SpringCloud 2020 版本后唯一支持的 netflix 组件
  • serviceregistry:服务注册,同上,spring-cloud-commons 针对 netflix-eurekaserver 部分也提供了二方实现 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 {

}

核心注解,被它修饰的 RestTemplateAsyncRestTemplate)就具有了 负载均衡 的能力,此注解的精髓是其上标注的限定符注解 @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

下一篇:【SpringCloud】 spring-cloud-loadbalancer 的实现 二


版权声明:本文为weixin_42189048原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。