springCloud的RestTemplate+@LoadBalanced注解实现负载均衡源码分析

学习springcluod的时候,有一个困惑,为什么RestTemplate上面@LoadBalanced注解,就能实现负载均衡,今天我们一起学习下源码,探索下springCloud底层的秘密:

第一步:在看源码之前我们先自己搭建一个消费者微服务(因为我们这里主要讲解的是springCloud的Ribbon负载均衡,所以注册中心和提供者这里就不再讲解了)

1、引入必要的maven依赖:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.9.RELEASE</version>
    <relativePath />
</parent>
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Edgware.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-eureka</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-ribbon</artifactId>
    </dependency>
</dependencies>

2、创建springboot的启动类:

@SpringBootApplication
@EnableDiscoveryClient
public class HelloApplicaton {

    //这里就是创建一个负载均衡的RestTemplate Bean
    @LoadBalanced
    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }


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

3、resource下配置启动文件:application.yml或者application.properties(具体的配置文件的作用我觉得应该不用我做解释)

server:
  port: 9091
spring:
  application:
    name: post-service
eureka:
  instance:
    hostname: localhost
  client:
    serviceUrl:
        defaultZone: http://localhost:1111/eureka/

4、创建消费者请求接口:

@RestController
public class HelloController {

   //注入前面创建的负载均衡的RestTemplate
    @Autowired
    public RestTemplate restTemplate;

    @RequestMapping(value = "/hello", method = RequestMethod.GET)
    public String postHello(){
     //使用有负载均衡能力的RestTemplate请求微服务
     return   restTemplate.getForEntity("http://HELLO-SERVICE/hello"
                ,String.class,"").getBody();
    }
}

注:这里的HELLO-SERVICE就是注册中心存在的微服务的名称,这个大家应该都很清楚;

这样一个具有负载均衡能力的消费者接口就创建成功了!

但是为什么被@LoadBalanced注解修饰的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让其拥有负载均衡的能力,查看org.springframework.cloud.client.loadbalancer包下面的class,我们很容易发现LoadBalancerAutoConfiguration这个类;

 
 
@Configuration//这是一个配置类
@ConditionalOnClass(RestTemplate.class)//这个配置文件加载必要条件是存在RestTemplate类和LoadBalancerClient
//Bean
@ConditionalOnBean(LoadBalancerClient.class)
@EnableConfigurationProperties(LoadBalancerRetryProperties.class)
public class LoadBalancerAutoConfiguration {

  //这个很重要,这里的restTemplates是所有的被@LoadBalanced注解的集合,这就是标记注解的作用(Autowired是可以集合注入的)
   @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);
   }

//生成一个LoadBalancerInterceptor的Bean
   @Configuration
   @ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
   static class LoadBalancerInterceptorConfig {
      @Bean
      public LoadBalancerInterceptor ribbonInterceptor(
            LoadBalancerClient loadBalancerClient,
            LoadBalancerRequestFactory requestFactory) {
         return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
      }

     //给注解了@LoadBalanced的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);
            }
         };
      }
   }

}

总结:现在我们应该大致知道@loadBalanced的作用了,就是起到一个标记RestTemplate的作用,当服务启动时,标记了的RestTemplate对象里面就会被自动加入LoadBalancerInterceptor拦截器,这样当RestTemplate像外面发起http请求时,会被LoadBalancerInterceptor的intercept函数拦截,而intercept里面又调用了LoadBalancerClient接口实现类execute方法,我们接着往下看

LoadBalancerInterceptor的intercept方法:
@Override
public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
      final ClientHttpRequestExecution execution) throws IOException {
 //这是以服务名为地址的原始请求:例:http://HELLO-SERVICE/hello
   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));
}

这里的LoadBalancerClient的实现是RibbonLoadBalancerClient,

public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException {
    //通过serviceId找到ILoadBalancer 的实现者,默认是ZoneAwareLoadBalancer
  //(RibbonClientConfiguration里面对ILoadBalancer的实现做的默认配置)
ILoadBalancer loadBalancer = this.getLoadBalancer(serviceId); Server server = this.getServer(loadBalancer); if (server == null) { throw new IllegalStateException( "No instances available for " + serviceId); } else {
        //将Server组装成RibbonServer,附加了一些额外的信息
        RibbonLoadBalancerClient.RibbonServer ribbonServer = new RibbonLoadBalancerClient.RibbonServer(serviceId, server, this.isSecure(server, serviceId), this.serverIntrospector(serviceId).getMetadata(server));
        return this.execute(serviceId, ribbonServer, request);
    }
}
//ZoneAwareLoadBalancer的Rule规则去选择Server(这里的规则是统一个zone的优先选择,这里返回的Server
//就是通过serviceId和Rule解析返回的带有IP:port形式的信息的Server)
protected Server getServer(ILoadBalancer loadBalancer) {
    return loadBalancer == null ? null : loadBalancer.chooseServer("default");
}

接着往下看:

public <T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException {
    Server server = null;
    if (serviceInstance instanceof RibbonLoadBalancerClient.RibbonServer) {
        server = ((RibbonLoadBalancerClient.RibbonServer)serviceInstance).getServer();
    }

    if (server == null) {
        throw new IllegalStateException("No instances available for " + serviceId);
    } else {
        RibbonLoadBalancerContext context = this.clientFactory.getLoadBalancerContext(serviceId);
        RibbonStatsRecorder statsRecorder = new RibbonStatsRecorder(context, server);

        try {
           //这一行很关键
            T returnVal = request.apply(serviceInstance);
            statsRecorder.recordStats(returnVal);
            return returnVal;
        } catch (IOException var8) {
            statsRecorder.recordStats(var8);
            throw var8;
        } catch (Exception var9) {
            statsRecorder.recordStats(var9);
            ReflectionUtils.rethrowRuntimeException(var9);
            return null;
        }
    }
}


public LoadBalancerRequest<ClientHttpResponse> createRequest(final HttpRequest request,
      final byte[] body, final ClientHttpRequestExecution execution) {
   return new LoadBalancerRequest<ClientHttpResponse>() {

      @Override
      public ClientHttpResponse apply(final ServiceInstance instance)
            throws Exception {
         HttpRequest serviceRequest = new ServiceRequestWrapper(request, instance, loadBalancer);
         if (transformers != null) {
            for (LoadBalancerRequestTransformer transformer : transformers) {
               serviceRequest = transformer.transformRequest(serviceRequest, instance);
            }
         }
         return execution.execute(serviceRequest, body);
      }

   };
}

这里需要注意ServiceRequestWrapper对象改写了getURL方法:

@Override
public URI getURI() {
   URI uri = this.loadBalancer.reconstructURI(
         this.instance, getRequest().getURI());
   return uri;
}

所以这里的reconstructURI实际上调用的是实现类RibbonLoadBalancerClient的reconstructURI方法:

public URI reconstructURI(ServiceInstance instance, URI original) {
    Assert.notNull(instance, "instance can not be null");
    String serviceId = instance.getServiceId();
    RibbonLoadBalancerContext context = this.clientFactory.getLoadBalancerContext(serviceId);
    //实际请求的server还是以IP:port的形式
    Server server = new Server(instance.getHost(), instance.getPort());
    IClientConfig clientConfig = this.clientFactory.getClientConfig(serviceId);
    ServerIntrospector serverIntrospector = this.serverIntrospector(serviceId);
    //是否需要转换为Https
    URI uri = RibbonUtils.updateToHttpsIfNeeded(original, clientConfig, serverIntrospector, server);
 
//  根据Server组装真正的URI  
    return context.reconstructURIWithServer(server, uri);
   }

execution.execute(serviceRequest, body)方法调用的是InterceptingRequestExecution的execute方法:
 
 
@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 {
         ClientHttpRequest delegate = requestFactory.createRequest(request.getURI(), request.getMethod());
         for (Map.Entry<String, List<String>> entry : request.getHeaders().entrySet()) {
            List<String> values = entry.getValue();
            for (String value : values) {
               delegate.getHeaders().add(entry.getKey(), value);
            }
         }
         if (body.length > 0) {
            StreamUtils.copy(body, delegate.getBody());
         }
      //这就是真正请求外部的地方
         return delegate.execute();
      }
   }
}
这里的request.getURI()调用的就是 ServiceRequestWrapper的getURL,也就是RibbonLoadBalancerClient的
reconstructURI方法;
总结:好了,至此springCloud的分析告一段落,这里也只是给给出了负载均衡的大纲,没有列出详细的细节,读者可根据大纲对照着查看细节,感谢您的观看,欢迎指错;

猜你喜欢

转载自blog.csdn.net/chengkui1990/article/details/80742225