第二章 SpringCloud Ribbon的源码解读

在SpringCloud中Ribbon的作用就是根据负载均衡策略从服务列表中选取合适的服务进行调用。

Ribbon调用服务的基本流程



上面这一张图从RestTemplate开始看起

1.当RestTemplate发起对远程服务的调用的时候,会调用InterceptingClieantRequest的executeInternal()方法。

2在InterceptingClieantRequest中会调用拦截器LoadBalanceInterceptor的intercept()拦截方法。

3.在LoadBalanceInterceptor的intercept()拦截方法中会调用LoadBalanceClient的实现了类RibbonLoadBalanceClient的execute方法。

4.RibbonLoadBalanceClient作为Ribbon的核心方法,在这个方法中会获取到ILoadBalancer的实例,通过ILoadBalancer实例获取到所有的服务列表和可用的服务列表,以及通过ILoadBalancer实例调用IRule的实例获取到符合策略的服务进行调用。

几个重要对象的介绍

1.RestTemplate
RestTemplate对象是对外发起请求的起点,封装请求参数发起请求,并解析返回结果,使返回结果可以直接拿来用。RestTemplate还会存储LoadBalanceInterceptor,在RestTemplate对外发起请求的时候会被LoadBalanceInterceptor拦截住,交由LoadBalanceInterceptor来处理

2.LoadBalanceInterceptor
LoadBalanceInterceptor会拦截RestTemplate对外发起的请求,但是LoadBalanceInterceptor没有做多余的处理,就是通过逻辑url获取到要调用的服务名,然后将服务名传给LoadBalanceClient,调用LoadBalanceClient来处理请求。

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

3.LoadBalanceClient
Ribbon负载均衡的客户端,可以把它看成一个控制器,因为在它这里根据根据服务名称选取ILoadBalacer,其中ILoadBalancer是根据负载均衡策略选出服务的关键;LoadBalanceClient还会通过RibbonStatsRecorder记录服务调用的情况。例如调用的服务耗时情况,服务的并发数,这些都是在负载策略选取服务的时候被用到的参考数据。
 

  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);
  }
  }
public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint) throws IOException {
    // 1. 获取ILoadBalancer
    ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
    
    // 2. 通过ILoadBalancer选择一个Server
    Server server = getServer(loadBalancer, hint);
    if (server == null) {
        throw new IllegalStateException("No instances available for " + serviceId);
    }
    RibbonServer ribbonServer = new RibbonServer(serviceId, server, isSecure(server,
            serviceId), serverIntrospector(serviceId).getMetadata(server));

    // 3. 对Server发起请求
    return execute(serviceId, ribbonServer, request);
}

protected Server getServer(ILoadBalancer loadBalancer, Object hint) {
    if (loadBalancer == null) {
        return null;
    }
    // Use 'default' on a null hint, or just pass it on?
    return loadBalancer.chooseServer(hint != null ? hint : "default");
}

首先要得到一个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;
            }
        }
    }

4.ILoadBalacer
ILoadBalacer可以从EurekaClient获取相应的服务列表,并且还会让IPing对象启动定时任务,获取可用服务列表。ILoadBalacer还会还会调用IRule来选出服务进行调用

public interface ILoadBalancer {

    public void addServers(List<Server> newServers);

    public Server chooseServer(Object key);
    
    public void markServerDown(Server server);
    
    public List<Server> getReachableServers();

    public List<Server> getAllServers();
}


5.IRule
IRule具体的负载均衡策略,根据具体的实现选出服务来进行调用。

public interface IRule{
    
    public Server choose(Object key);
    
    public void setLoadBalancer(ILoadBalancer lb);
    
    public ILoadBalancer getLoadBalancer();    
}

实现类有:

  • BestAviableRule
    跳过熔断的Server,在剩下的Server中选择并发请求最低的Server
  • ClientConfigEnabledRoundRobinRule、RoundRobinRule
    轮询
  • RandomRule
    随机选择
  • RetryRule
    可重试的策略,可以对其他策略进行重试,默认轮询重试
  • WeightedResponseTimeRule
    根据响应时间加权,响应时间越短权重越大
  • AvailabilityFilteringRule
    剔除因为连续链接、读失败或链接超过最大限制导致熔断的Server,在剩下读Server中进行轮询。

在启动Ribbon服务的时候,会加载LoadBalancerAutoConfiguration这个配置类,在这个配置类里面会维护被@LoadBalance修饰的RestTemplate对象,并对RestTemplate对象设置拦截器:

public class LoadBalancerAutoConfiguration {

    /**
      * 获取所有带有@LoadBalanced注解的restTemplate
     */
   @LoadBalanced
   @Autowired(required = false)
   private List<RestTemplate> restTemplates = Collections.emptyList();

    /**
     * 创建SmartInitializingSingleton接口的实现类。Spring会在所有
     * 单例Bean初始化完成后回调该实现类的afterSingletonsInstantiated()
     * 方法。在这个方法中会为所有被@LoadBalanced注解标识的
     * RestTemplate添加ribbon的自定义拦截器LoadBalancerInterceptor。
     */
   @Bean
   public SmartInitializingSingleton loadBalancedRestTemplateInitializer(
         final List<RestTemplateCustomizer> customizers) {
      return new SmartInitializingSingleton() {
         @Override
         public void afterSingletonsInstantiated() {
            for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
               for (RestTemplateCustomizer customizer : customizers) {
                  customizer.customize(restTemplate);
               }
            }
         }
      };
   }
    /**
     * 创建Ribbon自定义拦截器LoadBalancerInterceptor
     * 创建前提是当前classpath下不存在spring-retry。
     * 所以LoadBalancerInterceptor是默认的Ribbon拦截
     * 请求的拦截器。
     */
    @Configuration
    @ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
   static class LoadBalancerInterceptorConfig {
      @Bean
      public LoadBalancerInterceptor ribbonInterceptor(
            LoadBalancerClient loadBalancerClient,
            LoadBalancerRequestFactory requestFactory) {
         return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
      }

      /**
       * 添加拦截器具体方法。首先获取当前拦截器集合(List)
       * 然后将loadBalancerInterceptor添加到当前集合中
       * 最后将新的集合放回到restTemplate中。
       */
      @Bean
      @ConditionalOnMissingBean
      public RestTemplateCustomizer restTemplateCustomizer(
            final LoadBalancerInterceptor loadBalancerInterceptor) {
         return new RestTemplateCustomizer() {
            @Override
            public void customize(RestTemplate 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拦截器。

总结一下Ribbon的调用流程
1.启动消费者的时候LoadBalancerAutoConfiguration会给修饰有@LoadBalanced的RestTemplate Bean加上LoadBalancerInterceptor拦截器

这里有一篇写的非常好的Ribbon的原理:

https://www.jianshu.com/p/25ea0364c18e

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值