Riboon

Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡的工具。它是一个基于HTTP和TCP的客户端负载均衡器。

启用Ribbon需要增加的maven依赖:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>

restTemplate增加@LoadBlanced注解:

@EnableDiscoveryClient
@SpringBootApplication
public class EurekaConsumerRibbonApplication {

	@Bean
  @LoadBalanced
	public RestTemplate restTemplate(){
		return new RestTemplate();
	}

	public static void main(String[] args) {
		SpringApplication.run(EurekaConsumerRibbonApplication.class, args);
	}

}

测试接口:

@RestController
public class ConsumerRibbonController {

    @Autowired
    RestTemplate restTemplate;

    @GetMapping("/ribbon/consumer")
    public String consummer() {
        String serviceName = "eureka-client";
        return restTemplate.getForObject("http://" + serviceName + "/ls", String.class);
    }
}

这里的调用使用的是虚拟地址,即:微服务id+api路径

源码剖析

@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 {
}

@LoadBalanced标签是用来给RestTemplate标记,以使用LoadBalancerClient(负载均衡的客户端)来配置它。

查看下LoadBalancerClient的源码

/**
 * 表示是一个负载均衡器
 * Represents a client side load balancer
 * @author Spencer Gibb
 */
// 继承自ServiceInstanceChooser
public interface LoadBalancerClient extends ServiceInstanceChooser {

   /**
    * execute request using a ServiceInstance from the LoadBalancer for the specified
    * service
    * @param serviceId the service id to look up the LoadBalancer
    * @param request allows implementations to execute pre and post actions such as
    * incrementing metrics
    * @return the result of the LoadBalancerRequest callback on the selected
    * ServiceInstance
    */
   <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException;

   /**
    * execute request using a ServiceInstance from the LoadBalancer for the specified
    * service
    * @param serviceId the service id to look up the LoadBalancer
    * @param serviceInstance the service to execute the request to
    * @param request allows implementations to execute pre and post actions such as
    * incrementing metrics
    * @return the result of the LoadBalancerRequest callback on the selected
    * ServiceInstance
    */
   <T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException;

   /**
    * Create a proper URI with a real host and port for systems to utilize.
    * Some systems use a URI with the logical serivce name as the host,
    * such as http://myservice/path/to/service.  This will replace the
    * service name with the host:port from the ServiceInstance.
    * @param instance
    * @param original a URI with the host as a logical service name
    * @return a reconstructed URI
    */
   URI reconstructURI(ServiceInstance instance, URI original);
}

3个接口都是调用相关的。

看下ServiceInstanceChooser

/**
 * 使用负载均衡器选择一个服务,然后去发起请求
 * Implemented by classes which use a load balancer to choose a server to
 * send a request to.
 *
 * @author Ryan Baxter
 */
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);
}

choose方法的大致作用为:从LoadBalancer负载均衡器中为指定的服务(serviceId)选择一个服务实例(ServiceInstance) ,其实到这里我们大致已经明白了LoadBalancerClient的目的,就是通过choose方法选择要调用的服务实例,通过reconstructURI获取服务和主机和端口,然后通过execute执行服务的调用,而 RibbonLoadBalancerClient是对 LoadBalancerClient 的实现。

LoadBalancerAutoConfiguration

负载均衡的自动装配类为LoadBalancerAutoConfiguration,其在spring-cloud-commons之中定义并配置,可以查看该报的META-INF/spring.factories

/**
 * Auto configuration for Ribbon (client side load balancing).
 *
 * @author Spencer Gibb
 * @author Dave Syer
 * @author Will Tran
 */
@Configuration
@ConditionalOnClass(RestTemplate.class)
@ConditionalOnBean(LoadBalancerClient.class)
@EnableConfigurationProperties(LoadBalancerRetryProperties.class)
public class LoadBalancerAutoConfiguration {
   
   // 维护了一个被@LoadBalanced注解修饰的RestTemplate对象列表
   // 会调用RestTemplateCustomizer来给RestTemplate增加LoadBalancerInterceptor拦截器
   @LoadBalanced
   @Autowired(required = false)
   private List<RestTemplate> restTemplates = Collections.emptyList();

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

   @Autowired(required = false)
   private List<LoadBalancerRequestTransformer> transformers = Collections.emptyList();

   @Bean
   @ConditionalOnMissingBean
   public LoadBalancerRequestFactory loadBalancerRequestFactory(
         LoadBalancerClient loadBalancerClient) {
      return new LoadBalancerRequestFactory(loadBalancerClient, transformers);
   }

   @Configuration
   @ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
   static class LoadBalancerInterceptorConfig {
      // 用于实现对客户端发起请求时进行拦截,以实现客户端负载均衡
      @Bean
      public LoadBalancerInterceptor ribbonInterceptor(
            LoadBalancerClient loadBalancerClient,
            LoadBalancerRequestFactory requestFactory) {
         return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
      }
      // 用于给RestTemplate增加LoadBalancerInterceptor拦截器
      @Bean
      @ConditionalOnMissingBean
      public RestTemplateCustomizer restTemplateCustomizer(
            final LoadBalancerInterceptor loadBalancerInterceptor) {
         return new RestTemplateCustomizer() {
            // 给restTemp增加LoadBalancerInterceptor拦截器
            @Override
            public void customize(RestTemplate restTemplate) {
               List<ClientHttpRequestInterceptor> list = new ArrayList<>(
                     restTemplate.getInterceptors());
               list.add(loadBalancerInterceptor);
               restTemplate.setInterceptors(list);
            }
         };
      }
   }
	 // ......省略
}

主要是将所有的restTemplate设置上拦截器,现在暂时还没有看到维护这个restTemplate的代码 - -0

现在看看LoadBalancerInterceptor拦截器如何将一个普通的RestTemplate变成客户端负债均衡的:

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

实现了ClientHttpRequestInterceptor接口,当一个被@LoadBalanced注解修饰的RestTemplate对象对外发起Http请求时,会被LoadBalancerInterceptor类的intercept函数所拦截,此时执行LoadBalancerClient.execute()。

看下具体的负载均衡是如何实现的,所以看下LoadBalancerClient的实现类RibbonLoadBalancerClient

protected ILoadBalancer getLoadBalancer(String serviceId) {
	return this.clientFactory.getLoadBalancer(serviceId);
}

protected Server getServer(ILoadBalancer loadBalancer) {
  if (loadBalancer == null) {
    return null;
  }
  // 这里使用的是ILoadBalancer接口中定义的chooseServer
  // 而不是LoadBalancerClient接口中的choose函数
  return loadBalancer.chooseServer("default"); // TODO: better handling of key
}


@Override
public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException {
   // 通过传入的serviceId获取实例
   ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
   Server server = getServer(loadBalancer);
   if (server == null) {
      throw new IllegalStateException("No instances available for " + serviceId);
   }
   RibbonServer ribbonServer = new RibbonServer(serviceId, server, isSecure(server,
         serviceId), serverIntrospector(serviceId).getMetadata(server));

   return execute(serviceId, ribbonServer, request);
}

@Override
public <T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException {
   Server server = null;
   
   // .......

   RibbonLoadBalancerContext context = this.clientFactory
         .getLoadBalancerContext(serviceId);
   RibbonStatsRecorder statsRecorder = new RibbonStatsRecorder(context, server);
	 // .......
}
/**
 * Interface that defines the operations for a software loadbalancer. A typical
 * loadbalancer minimally need a set of servers to loadbalance for, a method to
 * mark a particular server to be out of rotation and a call that will choose a
 * server from the existing list of server.
 * 
 * @author stonse
 * 
 */
public interface ILoadBalancer {

   /**
    * Initial list of servers.
    * This API also serves to add additional ones at a later time
    * The same logical server (host:port) could essentially be added multiple times
    * (helpful in cases where you want to give more "weightage" perhaps ..)
    * 
    * @param newServers new servers to add
    */
   public void addServers(List<Server> newServers);
   
   /**
    * Choose a server from load balancer.
    * 
    * @param key An object that the load balancer may use to determine which server to return. null if 
    *         the load balancer does not use this parameter.
    * @return server chosen
    */
   public Server chooseServer(Object key);
   
   /**
    * To be called by the clients of the load balancer to notify that a Server is down
    * else, the LB will think its still Alive until the next Ping cycle - potentially
    * (assuming that the LB Impl does a ping)
    * 
    * @param server Server to mark as down
    */
   public void markServerDown(Server server);
   
   /**
    * @deprecated 2016-01-20 This method is deprecated in favor of the
    * cleaner {@link #getReachableServers} (equivalent to availableOnly=true)
    * and {@link #getAllServers} API (equivalent to availableOnly=false).
    *
    * Get the current list of servers.
    *
    * @param availableOnly if true, only live and available servers should be returned
    */
   @Deprecated
   public List<Server> getServerList(boolean availableOnly);

   /**
    * @return Only the servers that are up and reachable.
     */
    public List<Server> getReachableServers();

    /**
     * @return All known servers, both reachable and unreachable.
     */
   public List<Server> getAllServers();
}

看一下结构图

BaseLoadBalancer类实现了基础的负载均衡

那Ribbon默认采用的是哪个实现?

在RibbonClientConfiguration配置类中,可以知道整合时默认采用了ZoneAwareLoadBalancer来实现负载均衡。

@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);
}
posted @ 2020-06-02 20:37  江舟  阅读(341)  评论(0)    收藏  举报