springCloud 의 RestTemplate + @ LoadBalanced 주석 부하 균형 소스 분석 실현

31167 단어 spring
spring clud 를 공부 할 때 왜 Restmplate 위 @ LoadBalanced 주석 이 부하 균형 을 이 룰 수 있 는 지 곤 혹 스 러 웠 습 니 다. 오늘 우 리 는 함께 하 소스 코드 를 배우 고 spring Cloud 저층 의 비밀 을 탐색 합 니 다.
첫 번 째 단계: 소스 코드 를 보기 전에 우 리 는 먼저 소비자 마이크로 서 비 스 를 구축 합 니 다. (여기 서 주로 springCloud 의 Ribbon 부하 균형 을 설명 하기 때문에 등록 센터 와 공급 자 는 더 이상 설명 하지 않 습 니 다)
1. 필요 한 maven 의존 도입:
<parent>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-parentartifactId>
    <version>1.5.9.RELEASEversion>
    <relativePath />
parent>
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-dependenciesartifactId>
            <version>Edgware.RELEASEversion>
            <type>pomtype>
            <scope>importscope>
        dependency>
    dependencies>
dependencyManagement>
<dependencies>
    <dependency>
        <groupId>org.springframework.cloudgroupId>
        <artifactId>spring-cloud-starter-eurekaartifactId>
    dependency>
    <dependency>
        <groupId>org.springframework.cloudgroupId>
        <artifactId>spring-cloud-starter-ribbonartifactId>
    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. loadbancer 패키지 아래 의 class 를 보면 LoadBalancer AutoConfiguration 과 같은 종 류 를 쉽게 발견 할 수 있 습 니 다.
 
  
@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 restTemplates = Collections.emptyList();

   @Bean
   public SmartInitializingSingleton loadBalancedRestTemplateInitializer(
         final List 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 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 list = new ArrayList<>(
                     restTemplate.getInterceptors());
               list.add(loadBalancerInterceptor);
               restTemplate.setInterceptors(list);
            }
         };
      }
   }

}
요약: 이제 우 리 는 @ loadBalanced 의 역할 을 대체적으로 알 아야 합 니 다. 바로 RestTemplate 를 표시 하 는 역할 을 합 니 다. 서비스 가 시 작 될 때 표 시 된 RestTemplate 대상 에 LoadBalancer Interceptor 차단기 가 자동 으로 추 가 됩 니 다. 그러면 RestTemplate 가 외부 에서 http 요청 을 할 때 LoadBalancer Interceptor 의 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));
}

여기 LoadBalancer Client 의 실현 은 Ribbon LoadBalancer Client 입 니 다.
public  T execute(String serviceId, LoadBalancerRequest 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 execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest 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 createRequest(final HttpRequest request,
      final byte[] body, final ClientHttpRequestExecution execution) {
   return new LoadBalancerRequest() {

      @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 는 실제 적 으로 클래스 Ribbon LoadBalancerClient 를 실현 하 는 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> entry : request.getHeaders().entrySet()) {
            List values = entry.getValue();
            for (String value : values) {
               delegate.getHeaders().add(entry.getKey(), value);
            }
         }
         if (body.length > 0) {
            StreamUtils.copy(body, delegate.getBody());
         }

여기 request. getURI () 가 호출 한 것 은
ServiceRequestWrapper 의 getURL, 즉 RibbonLoadBalancerClient 의
      //            
         return delegate.execute();
      }
   }
}

결론: 자, 이로써 springCloud 의 분석 은 일 단락 되 었 습 니 다. 여기 서도 부하 균형 에 대한 대강 을 제 시 했 을 뿐 상세 한 세부 사항 을 열거 하지 않 았 습 니 다. 독 자 는 대강 에 따라 세부 사항 을 대조 하여 볼 수 있 습 니 다. 시청 해 주 셔 서 감사합니다.

좋은 웹페이지 즐겨찾기