Loading

Ribbon中负载均衡的源码解析

Ribbon中的关键组件

ServerList:可以响应客户端的特定服务的服务器列表。
ServerListFilter:可以动态获得的具有所需特征的候选服务器列表的过滤器。
ServerListUpdater:用于执行动态服务器列表更新。
Rule:负载均衡策略,用于确定从服务器列表返回哪个服务器。
Ping:客户端用于快速检查服务器当时是否处于活动状态。
LoadBalancer:负载均衡器,负责负载均衡调度的管理。 

 @LoadBalanced注解

使用Ribbon完成客户端负载均衡往往是从一个注解开始的
/**
* 基于Ribbon的服务调用与负载均衡
*/
@LoadBalanced
@Bean
public RestTemplate getRestTemplate() {
    return new RestTemplate();
}
这个注解的主要作用是什么呢,查看源码 
/**
* Annotation to mark a RestTemplate bean to be configured to use a 
LoadBalancerClient
* @author Spencer Gibb
*/
@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface LoadBalanced {
}
通过注释可以知道@LoadBalanced注解是用来给RestTemplate做标记,方便我们对RestTemplate添加一个LoadBalancerClient,以实现客户端负载均衡。

自动装配

根据SpringBoot中的自动装配规则可以在 spring-cloud-netflix-ribbon-2.1.0.RELEASE.jar 中可以找到 spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\org.springframew
ork.cloud.netflix.ribbon.RibbonAutoConfiguration
找到自动装配的类RibbonAutoConfifiguration
@Configuration
@Conditional({RibbonAutoConfiguration.RibbonClassesConditions.class})
@RibbonClients
@AutoConfigureAfter(
    name = {"org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration"} )
@AutoConfigureBefore({LoadBalancerAutoConfiguration.class, 
AsyncLoadBalancerAutoConfiguration.class})
@EnableConfigurationProperties({RibbonEagerLoadProperties.class, 
ServerIntrospectorProperties.class})
public class RibbonAutoConfiguration {
 @Bean
    public SpringClientFactory springClientFactory() {
        SpringClientFactory factory = new SpringClientFactory();
        factory.setConfigurations(this.configurations);
        return factory;
   }
    
    @Bean
    @ConditionalOnMissingBean({LoadBalancerClient.class})
    public LoadBalancerClient loadBalancerClient() {
        return new RibbonLoadBalancerClient(this.springClientFactory());
   }
    //省略
}
通过 RibbonAutoConfiguration 引入了 LoadBalancerAutoConfiguration 配置类

负载均衡调用

@Configuration
@ConditionalOnClass({RestTemplate.class})
@ConditionalOnBean({LoadBalancerClient.class})
@EnableConfigurationProperties({LoadBalancerRetryProperties.class})
public class LoadBalancerAutoConfiguration {
    @LoadBalanced
    @Autowired(
        required = false
   )
    private List<RestTemplate> restTemplates = Collections.emptyList();
    @Autowired(
        required = false
   )
    private List<LoadBalancerRequestTransformer> transformers =
Collections.emptyList();
 public LoadBalancerAutoConfiguration() {
   }
    @Bean
    public SmartInitializingSingleton
loadBalancedRestTemplateInitializerDeprecated(ObjectProvider<List<RestTemplateCu
stomizer>> restTemplateCustomizers) {
        return () -> {
            restTemplateCustomizers.ifAvailable((customizers) -> {
                Iterator var2 = this.restTemplates.iterator();
                while(var2.hasNext()) {
                    RestTemplate restTemplate = (RestTemplate)var2.next();
                    Iterator var4 = customizers.iterator();
                    while(var4.hasNext()) {
                        RestTemplateCustomizer customizer = (RestTemplateCustomizer)var4.next();
                        customizer.customize(restTemplate);
                   }
               }
           });
       };
   }
    @Bean
    @ConditionalOnMissingBean
    public LoadBalancerRequestFactory
loadBalancerRequestFactory(LoadBalancerClient loadBalancerClient) {
        return new LoadBalancerRequestFactory(loadBalancerClient, 
this.transformers);
   }
    @Configuration
    @ConditionalOnClass({RetryTemplate.class})
    public static class RetryInterceptorAutoConfiguration {
        public RetryInterceptorAutoConfiguration() {
       }
        @Bean
        @ConditionalOnMissingBean
        public RetryLoadBalancerInterceptor ribbonInterceptor(LoadBalancerClient
loadBalancerClient, LoadBalancerRetryProperties properties, 
LoadBalancerRequestFactory requestFactory, LoadBalancedRetryFactory
loadBalancedRetryFactory) {
            return new RetryLoadBalancerInterceptor(loadBalancerClient, 
properties, requestFactory, loadBalancedRetryFactory);
       }
        @Bean
        @ConditionalOnMissingBean
        public RestTemplateCustomizer
restTemplateCustomizer(RetryLoadBalancerInterceptor loadBalancerInterceptor) {
            return (restTemplate) -> {
                List<ClientHttpRequestInterceptor> list = new
ArrayList(restTemplate.getInterceptors());
                list.add(loadBalancerInterceptor);
                restTemplate.setInterceptors(list);
           };
       }
   }
    @Configuration
    @ConditionalOnClass({RetryTemplate.class})
    public static class RetryAutoConfiguration {
        public RetryAutoConfiguration() {
       }
        @Bean
        @ConditionalOnMissingBean
        public LoadBalancedRetryFactory loadBalancedRetryFactory() {
            return new LoadBalancedRetryFactory() {
           };
       }
   }
    @Configuration
   
@ConditionalOnMissingClass({"org.springframework.retry.support.RetryTemplate"})
    static class LoadBalancerInterceptorConfig {
        LoadBalancerInterceptorConfig() {
       }
        @Bean
        public LoadBalancerInterceptor ribbonInterceptor(LoadBalancerClient
loadBalancerClient, LoadBalancerRequestFactory requestFactory) {
            return new LoadBalancerInterceptor(loadBalancerClient, 
requestFactory);
       }
        @Bean
        @ConditionalOnMissingBean
        public RestTemplateCustomizer
restTemplateCustomizer(LoadBalancerInterceptor loadBalancerInterceptor) {
            return (restTemplate) -> {
                List<ClientHttpRequestInterceptor> list = new
ArrayList(restTemplate.getInterceptors());
                list.add(loadBalancerInterceptor);
                restTemplate.setInterceptors(list);
           };
       }
   }
}
在该自动化配置类中,主要做了下面三件事:
  创建了一个 LoadBalancerInterceptor 的Bean,用于实现对客户端发起请求时进行拦截,以实现客户端负载均衡。
  创建了一个 RestTemplateCustomizer 的Bean,用于给 RestTemplate 增加LoadBalancerInterceptor 拦截器。
  维护了一个被 @LoadBalanced 注解修饰的 RestTemplate 对象列表,并在这里进行初始化,通过调用 RestTemplateCustomizer 的实例来给需要客户端负载均衡的 RestTemplate 增加LoadBalancerInterceptor 拦截器。 
public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {
    private LoadBalancerClient loadBalancer;
    private LoadBalancerRequestFactory requestFactory;
    public LoadBalancerInterceptor(LoadBalancerClient loadBalancer, 
LoadBalancerRequestFactory requestFactory) {
        this.loadBalancer = loadBalancer;
        this.requestFactory = requestFactory;
   }
    public LoadBalancerInterceptor(LoadBalancerClient loadBalancer) {
        this(loadBalancer, new LoadBalancerRequestFactory(loadBalancer));
   }
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, 
ClientHttpRequestExecution execution) throws IOException {
        URI originalUri = request.getURI();
        String serviceName = originalUri.getHost();
        Assert.state(serviceName != null, "Request URI does not contain a valid 
hostname: " + originalUri);
        return (ClientHttpResponse)this.loadBalancer.execute(serviceName, 
this.requestFactory.createRequest(request, body, execution));
   }
}

通过源码以及之前的自动化配置类,我们可以看到在拦截器中注入了 LoadBalancerClient 的实现。当一个被 @LoadBalanced 注解修饰的 RestTemplate 对象向外发起HTTP请求时,会被LoadBalancerInterceptor 类的 intercept 函数所拦截。由于我们在使用RestTemplate时候采用了服务名作为host,所以直接从 HttpRequest 的URI对象中通过getHost()就可以拿到服务名,然后调用execute 函数去根据服务名来选择实例并发起实际的请求。

分析到这里, LoadBalancerClient 还只是一个抽象的负载均衡器接口,所以我们还需要找到它的具体实现类来进一步分析。通过查看ribbon的源码,我们可以很容易的在org.springframework.cloud.netflix.ribbon 包下找到对应的实现类:

RibbonLoadBalancerClient 。 

public class RibbonLoadBalancerClient implements LoadBalancerClient {
    public ServiceInstance choose(String serviceId) {
        Server server = this.getServer(serviceId);
        return server == null ? null : new
RibbonLoadBalancerClient.RibbonServer(serviceId, server, this.isSecure(server, 
serviceId), this.serverIntrospector(serviceId).getMetadata(server));
   }
    
    public <T> T execute(String serviceId, LoadBalancerRequest<T> request) 
throws IOException {
        ILoadBalancer loadBalancer = this.getLoadBalancer(serviceId);
        Server server = this.getServer(loadBalancer);
        if (server == null) {
            throw new IllegalStateException("No instances available for " +
serviceId);
       } else {
            RibbonLoadBalancerClient.RibbonServer ribbonServer = new
RibbonLoadBalancerClient.RibbonServer(serviceId, server, this.isSecure(server, 
serviceId), this.serverIntrospector(serviceId).getMetadata(server));
            return this.execute(serviceId, ribbonServer, request);
       }
   }
    
    protected Server getServer(String serviceId) {
        return this.getServer(this.getLoadBalancer(serviceId));
   }
    
    protected Server getServer(ILoadBalancer loadBalancer) {
        return loadBalancer == null ? null : 
loadBalancer.chooseServer("default");
   }
    
    protected ILoadBalancer getLoadBalancer(String serviceId) {
        return this.clientFactory.getLoadBalancer(serviceId);
   }
    
    //省略...
}
ServiceInstance choose(String serviceId):根据传入的服务id,从负载均衡器中为指定的服务选择一个服务实例。
T execute(String serviceId, LoadBalancerRequest request):根据传入的服务id,指定的负载均衡器中的服务实例执行请求。
T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest request):根据传入的服务实例,执行请求。
从 RibbonLoadBalancerClient 代码可以看出,实际负载均衡的是通过 ILoadBalancer 来实现的:
@Bean
@ConditionalOnMissingBean
public ILoadBalancer ribbonLoadBalancer(IClientConfig config, ServerList<Server>
serverList, ServerListFilter<Server> serverListFilter, IRule rule, IPing ping, 
ServerListUpdater serverListUpdater) {
    return (ILoadBalancer)(this.propertiesFactory
   .isSet(ILoadBalancer.class, this.name) ? (ILoadBalancer)this.propertiesFactory
   .get(ILoadBalancer.class, config, this.name) : new
ZoneAwareLoadBalancer(config, rule, ping, serverList, serverListFilter, 
serverListUpdater));
}

 

posted @ 2021-07-27 14:23  1640808365  阅读(126)  评论(0编辑  收藏  举报