Spring Cloud Ribbon: 【Spring Cloud学习】-1.Spring Cloud Ribbon 实现负载均衡
1.RestTemplate 调用如何负载均衡?
通过上面的调用流程可以发现,在 createRequest 时,创建了 InterceptingClientHttpRequest
@Override
protected ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod, ClientHttpRequestFactory requestFactory) {
return new InterceptingClientHttpRequest(requestFactory, this.interceptors, uri, httpMethod);
}
所以在调用 request.execute 方法时,实际调用的是 InterceptingClientHttpRequest.execute,InterceptingClientHttpRequest 的类图如下
所以会先调用 AbstractClientHttpRequest 和 AbstractBufferingClientHttpRequest,最终调用 InterceptingClientHttpRequest 时,构造了一个调用链 execution
protected final ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput) throws IOException {
InterceptingRequestExecution requestExecution = new InterceptingRequestExecution();
return requestExecution.execute(this, bufferedOutput);
}
InterceptingRequestExecution 的构造函数中,传入一个 iterator 迭代器
public InterceptingRequestExecution() {
this.iterator = interceptors.iterator();
}
所以执行 requestExecution.execute 时,会调用 interceptor 的拦截方法
@Override
public ClientHttpResponse execute(HttpRequest request, byte[] body) throws IOException {
if (this.iterator.hasNext()) {
ClientHttpRequestInterceptor nextInterceptor = this.iterator.next();
return nextInterceptor.intercept(request, body, this);
}
else {
// ...
}
}
这里的 interceptor 实际上是 LoadBalancerInterceptor, 所以最终通过 LoadBalancerInterceptor 实现了负载均衡,选择一个服务端进行调用。
2.关键类是如何初始化的?
通过上面的分析,发现有几个关键的类:
- InterceptingClientHttpRequestFactory:创建了 InterceptingClientHttpRequest
- InterceptingClientHttpRequest:调用了requestExecution.execute方法,最终执行了拦截器
- LoadBalancerInterceptor:实现负载均衡的拦截器,通过构造函数传入 InterceptingRequestExecution 中
- RibbonLoadBalancerClient: Ribbon 负载均衡客户端
- ZoneAwareLoadBalancer:具体的负载均衡选择器
InterceptingClientHttpRequestFactory
RestTemplate 继承自 InterceptingHttpAccessor,所以在 createRequest 中调用 getRequestFactory 方法时,会调用到 InterceptingHttpAccessor.getRequestFactory
public ClientHttpRequestFactory getRequestFactory() {
// 获取 interceptors
List<ClientHttpRequestInterceptor> interceptors = getInterceptors();
if (!CollectionUtils.isEmpty(interceptors)) {
ClientHttpRequestFactory factory = this.interceptingRequestFactory;
if (factory == null) {
// 初始化一个 InterceptingClientHttpRequestFactory
factory = new InterceptingClientHttpRequestFactory(super.getRequestFactory(), interceptors);
this.interceptingRequestFactory = factory;
}
return factory;
}
else {
return super.getRequestFactory();
}
}
InterceptingClientHttpRequest
InterceptingClientHttpRequestFactory 继承自 AbstractClientHttpRequestFactoryWrapper,所以最终是在 InterceptingClientHttpRequestFactory.createRequest 方法中创建了 InterceptingClientHttpRequest 对象
@Override
protected ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod, ClientHttpRequestFactory requestFactory) {
return new InterceptingClientHttpRequest(requestFactory, this.interceptors, uri, httpMethod);
}
LoadBalancerInterceptor
负载均衡有一个自动装配类 LoadBalancerAutoConfiguration,在这个类中有一个 LoadBalancerInterceptorConfig 的内部类,它通过 ribbonInterceptor 方法声明了一个 LoadBalancerInterceptor 对象,然后又通过 restTemplateCustomizer 方法声明了一个 RestTemplateCustomizer对象。
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
static class LoadBalancerInterceptorConfig {
@Bean
public LoadBalancerInterceptor ribbonInterceptor(
LoadBalancerClient loadBalancerClient,
LoadBalancerRequestFactory requestFactory) {
// 声明一个 LoadBalancerInterceptor 拦截器对象
return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
}
@Bean
@ConditionalOnMissingBean
public RestTemplateCustomizer restTemplateCustomizer(
final LoadBalancerInterceptor loadBalancerInterceptor) {
// 这里声明了一个匿名类,通过lambda实现了customize方法
return restTemplate -> {
List<ClientHttpRequestInterceptor> list = new ArrayList<>(
restTemplate.getInterceptors());
list.add(loadBalancerInterceptor);
restTemplate.setInterceptors(list);
};
}
}
RestTemplateCustomizer 是一个接口,它里面只有一个 customize 方法
public interface RestTemplateCustomizer {
void customize(RestTemplate restTemplate);
}
而在 loadBalancedRestTemplateInitializerDeprecated 方法中,这里遍历所有的 customizers,调用它的 customize 方法,所以最终将 loadBalancerInterceptor 放入到了 restTemplate 的 interceptors 字段中。
@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);
}
}
});
}
RibbonLoadBalancerClient
在 RibbonAutoConfiguration#loadBalancerClient 中进行声明
@Bean
@ConditionalOnMissingBean(LoadBalancerClient.class)
public LoadBalancerClient loadBalancerClient() {
return new RibbonLoadBalancerClient(springClientFactory());
}
ZoneAwareLoadBalancer
在 RibbonClientConfiguration#ribbonLoadBalancer 中进行声明
@Bean
@ConditionalOnMissingBean
public ILoadBalancer ribbonLoadBalancer(IClientConfig config,
ServerList<Server> serverList, ServerListFilter<Server> serverListFilter,
IRule rule, IPing ping, ServerListUpdater serverListUpdater) {
if (this.propertiesFactory.isSet(ILoadBalancer.class, name)) {
return this.propertiesFactory.get(ILoadBalancer.class, config, name);
}
return new ZoneAwareLoadBalancer<>(config, rule, ping, serverList,
serverListFilter, serverListUpdater);
}