Ribbon运行原理

Ribbon的核心工作原理

接口描述默认实现类
IClientConfig定义Ribbon管理配置得接口DefaultClientConfigImpl
IRule定义Ribbon中负载均衡策略的接口ZoneAvoidanceRule
IPing定义顶起ping服务检查可用性的接口DummyPing
ServerList定义获取服务列表方法的接口ConfigurationBasedServerList
ServerListFilter定义特定期望获取服务列表方法的接口ZonePreferenceServerListFilter
ILoadBalancer定义负载均衡选择服务的核心老方法的接口ZoneAwareLoadBalancer
ServerListUpdater为DynamicServerListLoadBalance定义动态更新服务猎鸟的接口PollingServerListUpdater

ribbon的能够起作用都是基于以上的接口的支撑,ribbon的负载均衡是如何做到的呢?在之前的例子中我们已经使用到了RestTemplate进行负载均衡的请求,加了@LoadBalanced的注解之后就可以实现负载均衡。注解的代码如下:

/**
 * 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 {
}
复制代码

根据该注解的注释可以知道,标志了该注解的RestTemplate是负载均衡的客户端,从代码可以看到该注解是一个接口,该接口的具体实现类是RibbonLoadBalancerClient,初次之外还拓展了ServiceInstanceChooser接口

  • LoadBalancerClient.java

      /**
       * Represents a client side load balancer
       * @author Spencer Gibb
       */
      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.java

    public interface ServiceInstanceChooser {

      /**
       * Choose a ServiceInstance from the LoadBalancer for the specified service
       * @param serviceId the service id to look up the LoadBalancer
       * @return a ServiceInstance that matches the serviceId
       */
      ServiceInstance choose(String serviceId);
    复制代码

    }

  1. ServiceInstance choose(String serviceId):该方法主要是根据负载均衡器选择一个服务实例。

  2. T execute(String serviceId, LoadBalancerRequest request) throws IOException

  3. T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest request) throws IOException:2.3这两个接口主要是为负载均衡选怎的服务执行请求,构建一个服务,3是2的具体实现。、

  4. URI reconstructURI(ServiceInstance instance, URI original):构建一个url,该方法主要使用具有逻辑服务名称的作为URI座位host。使用主机ip和port构建特定的URI提供给ribbon使用。

上面的接口是很重要的,这些接口主要在以下的java类内部实现:

    @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<RestTemplateCustomizer>> 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);
                };
            }
        }
    }
复制代码

从该类的注解可以发现当前类的加载时机必须存在restTemplate类,工程环境中必须初始化了LoadBalancerClient的实现类。整个配置类只截取了重要的部分,其中,LoadBalancerRequestFactory用于创建LoadBalancerRequest供LoadBalancerInterceptor使用。LoadBalacerInterceptorConfig中维护了LoadBalacerInterceptor雨RestTemplateCustomizer的实例。

  • LoadBalacerInterceptor:拦截每一次HTTP请求,将请求绑定进Ribbon负载均衡生命周期。

  • RestTemplateCustomizer:为每个RestTemplate绑定LoadBalacerInterceptor拦截器,这个也是重要的部分,其源码如下:

    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) {
      	// for backwards compatibility
      	this(loadBalancer, new LoadBalancerRequestFactory(loadBalancer));
      }
    
      @Override
      public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
      		final ClientHttpRequestExecution execution) throws IOException {
      	final URI originalUri = request.getURI();
      	String serviceName = originalUri.getHost();
      	Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri);
      	return this.loadBalancer.execute(serviceName, requestFactory.createRequest(request, body, execution));
      }
    复制代码

    }

      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);
      }
      }
    复制代码

首先要得到一个ILoadBalancer,用它去得到一个Server(该server就是服务实例的封装体,也是实现负载均衡的地方)。具体的代码如下:

   protected Server getServer(ILoadBalancer loadBalancer) {
        return loadBalancer == null ? null : loadBalancer.chooseServer("default");
    }
复制代码

查看负载均衡的方法可以看大其源码如下:

 public Server chooseServer(Object key) {
        if (this.counter == null) {
            this.counter = this.createCounter();
        }

        this.counter.increment();
        if (this.rule == null) {
            return null;
        } else {
            try {
                return this.rule.choose(key);
            } catch (Exception var3) {
                logger.warn("LoadBalancer [{}]:  Error choosing server for key {}", new Object[]{this.name, key, var3});
                return null;
            }
        }
    }
复制代码

这个地方的this.rule.choose(key)其实就是我们定义的IRule,实现了HTTP请求与负载均衡的结合。

负载均衡的策略的类继承图

Ribbon是通过ILoadBalancer来关联IRule的,ILoadBalancer的chooserServer方法会转换为掉牙IRule的choose方法,抽象类AbstractLoadBalancerRule实现了这两个方法,实现了ILoadBalancer与IRule关联。

转载于:https://juejin.im/post/5cd0cf6751882541e37e6a76

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值