【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