Why can the RestTemplate in the Spring Cloud LoadBalancer load balancing policy be annotated with @ LoadBalanced to access the service with the service name

Why can the RestTemplate in the Spring Cloud LoadBalancer load balancing policy be annotated with @ LoadBalanced to access the service with the service name


Why learn this? You don't want to pretend two more during the interview and force you to get two thousand yuan more? Robin load balancing analysis, please see my Robin load balancing article.

**.
When SpringBoot starts, we know that the automatic configuration class will be loaded according to the factory mechanism. For @ LoadBalanced, the corresponding loading class is LoadBalancerAutoConfiguration. Let's go into this class and pay attention to my notes!

@Configuration
@ConditionalOnClass({RestTemplate.class})
@ConditionalOnBean({LoadBalancerClient.class})
@EnableConfigurationProperties({LoadBalancerRetryProperties.class})
public class LoadBalancerAutoConfiguration {
    @LoadBalanced
    @Autowired(
        required = false
    )
    //1. Get all resttemplates modified by @ LoadBalanced annotation in the context object!
    private List<RestTemplate> restTemplates = Collections.emptyList();
    @Autowired(
        required = false
    )
    private List<LoadBalancerRequestTransformer> transformers = Collections.emptyList();

    public LoadBalancerAutoConfiguration() {
    }

    @Bean
    public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {
    //The object < customtemplaterrest2 > in the context collection exists
        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();
                       	//3. Traverse the RestTemplate set obtained in step 1, and use the RestTemplateCustomizer set to customize each RestTemplate.
                        customizer.customize(restTemplate);
                    }
                }

            });
        };
    }

What does the RestTemplateCustomizer do when customizing? OK, let's continue. The loadbalancerinterpectorconfig configuration class in this class defines RestTemplateCustomizer. Here is the source code

@Configuration
//1. Condition note: loadbalancerinterpectorconfig will take effect only when there is no RetryTemolate in ClassLoader (we don't care about it)
@ConditionalOnMissingClass({"org.springframework.retry.support.RetryTemplate"})
    static class LoadBalancerInterceptorConfig {
        LoadBalancerInterceptorConfig() {
        }

        @Bean
        //2. Define LoadBalancerInterceptor Bean. This interceptor inherits clienthttprequeinterceptor and can be added to the interceptor list of RestTemplate.
        public LoadBalancerInterceptor ribbonInterceptor(LoadBalancerClient loadBalancerClient, LoadBalancerRequestFactory requestFactory) {
            return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
        }

        @Bean
        @ConditionalOnMissingBean
        //3. The resttemplatecustomizer bean defined will exist in the restTemplateCustomizers in the loadbalancenautoconfiguration configuration class
        public RestTemplateCustomizer restTemplateCustomizer(final LoadBalancerInterceptor loadBalancerInterceptor) {
            return (restTemplate) -> {
                List<ClientHttpRequestInterceptor> list = new ArrayList(restTemplate.getInterceptors());
                list.add(loadBalancerInterceptor); //4. Add LoadBalanceInterceptor interceptor interceptor in the interceptor of RestTemplate!
                restTemplate.setInterceptors(list);
            };
        }
    }

OK, to sum up, RestTemplate directly decorated with @ LoadBalanced will be added with a LoadBalanceInterceptor interceptor!!! OK! So what does this interceptor do? Let's keep looking down.

public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {
    private LoadBalancerClient loadBalancer;
    private LoadBalancerRequestFactory requestFactory;

    public LoadBalancerInterceptor(LoadBalancerClient loadBalancer, LoadBalancerRequestFactory requestFactory) { 
    // 1. Constructor! The constructor needs the LoadBalancerClient and LoadBalancerRequestFactory parameters (they will be constructed in loadbalancenautoconfiguration by default and can be overwritten by developers). The former makes a real service call according to the load balancerrequest and the service name; The latter is a factory, which is responsible for constructing the load balancing request LoadBalancerRequest. In the construction process, the LoadBalancerRequestTransformer will be used to make some custom transformations to the request.
        this.loadBalancer = loadBalancer;
        this.requestFactory = requestFactory;
    }

    public LoadBalancerInterceptor(LoadBalancerClient loadBalancer) {
        this(loadBalancer, new LoadBalancerRequestFactory(loadBalancer));
    }

    public ClientHttpResponse intercept(final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution) throws IOException {
        URI originalUri = request.getURI();
        //2. The service name uses the Host information in the URI.
        String serviceName = originalUri.getHost();
        Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri);
        //3. Use LoadBalancerClient client load balancer to make real service calls!!!!
        return (ClientHttpResponse)this.loadBalancer.execute(serviceName, this.requestFactory.createRequest(request, body, execution));
    }
}

Summary: OK, now we know that the request sent by the RestTemplate directly modified by @ LoadBalanced will finally be intercepted by the LoadBalanceInterceptor! Finally, the LoadBalancerClient sends the real request. Let's take a brief look at LoadBalancerClient. LoadBalancerClient, also known as client load balancer, performs real load balancing operations according to load balancing requests and service names. Look at the source code:

public interface LoadBalancerClient extends ServiceInstanceChooser {
	/**
	serviceId:service name
	request: Load balancing request
	Return value: Based on the result returned by the selected ServiceInstance under the load balancing request
	**/
    <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException;

	/**
	serviceId:service name
	serviceInstance Service instance
	request: Load balancing request
	Return value: Based on the result returned by the selected ServiceInstance under the load balancing request
	**/
    <T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException;

	/**
	Use the host and port in ServiceInstance to construct the real URI.
	For example! http: / / my service name / path/to/service the corresponding hsot of "my service name" in the URI is 127.0.0.1 and port is 8083. Finally, it will be constructed into a real URI!!!!
	Parameters for this method:
		instance,Service instance
		original,Old URI
		Return value: URI after construction
	**/
    URI reconstructURI(ServiceInstance instance, URI original);
}

Why are there two overloaded methods, one of which has no serviceinstance object. (about what the serviceinstance object is and what is familiar with it, let's Baidu. In short, let's find the service through the service and save the found service in memory in the form of serviceinstance object). If we call a method without serviceinstance parameter, our execute method will use the load balancing algorithm to get a serviceinstance through the choose method!!! Then call the execute method with serviceinstance!

Well, it's OK when the interview reaches here. Let me summarize it for you!!!
@The RestTemplate modified by the LoadBalanced annotation will be wrapped by the RestTemplateCustomizer and forcibly added with an interceptor. When sending a request using the RestTemplate + service name, it will be intercepted by the loadbalanceintercept interceptor. After interception, the service instance will be obtained according to the service name, that is, ServiceInstance!!! This ServiceInstance object encapsulates all the properties of the service, including the Host and Port we care about most!! After the interceptor obtains the service instance, the LoadBalancerClient object will make a formal service call! The three interfaces in LoadBalancerClient are on the top. I just sent it out. You should not forget it. OK, that's why RestTemplate can access the underlying principle of the service with the @ LoadBalanced annotation!

For other knowledge, such as custom load balancing algorithm and @ LoadBalancerClient annotation, I will repeat it here. After reading it, everyone gave a praise. It's not easy to code...

Keywords: Java Load Balance Spring Spring Cloud

Added by suave4u on Mon, 31 Jan 2022 03:37:28 +0200