侧边栏壁纸
博主头像
soulballad博主等级

技术文章记录及总结

  • 累计撰写 169 篇文章
  • 累计创建 26 个标签
  • 累计收到 4 条评论

目 录CONTENT

文章目录

【源码分析-Spring-Cloud】-1.Spring Cloud Ribbon 负载均衡实现原理

soulballad
2020-08-10 / 0 评论 / 0 点赞 / 77 阅读 / 5,106 字
温馨提示:
本文最后更新于 2022-03-03,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

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);
}
0

评论区