Spring Cloud micro-services technology stack (E): Client load balancing Spring Cloud Ribbon part of the source code analysis

In the second quarter " : the Spring Micro Cloud services technology stack (two) to build high availability Eureka Server, service registration and discovery time", we build a service consumers to use client load balancing, just in time to create RestTemplatethe object's code on the plus @LoadBalancednotes, as simple as a step to achieve the client load balancing function, then it is how to achieve it? Starting from the basic source of this article to explore the principles of client load balancing.

Source code analysis

In order for the client with the ability to load balance, we are in the code RestTemplateto Springmanage time, will add @LoadBalancedannotations, as shown in the following code:

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

After adding this annotation, then RestTemplatean instance object to have the ability to load balance, why add this comment after the RestTemplateinstance of the object to have the load balancing capabilities of it? We enter into the annotated source code to find out.

package org.springframework.cloud.client.loadbalancer;

import org.springframework.beans.factory.annotation.Qualifier;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 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 {
}

From the annotated code view, and general comments it does not make much difference, we can understand it from the comments: "This annotation is used to RestTemplatemark, to use client load balancing ( LoadBalancerClient) to configure it. " From the comments, we capture an important keyword that is load balancing the client - LoadBalancerClientwe search in the source code LoadBalancerClientand found that it is Spring Cloudan interface defined:

package org.springframework.cloud.client.loadbalancer;

import org.springframework.cloud.client.ServiceInstance;

import java.io.IOException;
import java.net.URI;

public interface LoadBalancerClient extends ServiceInstanceChooser {

	<T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException;

	<T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException;
	
	URI reconstructURI(ServiceInstance instance, URI original);
}

It inherits the ServiceInstanceChooserinterface, which has an abstract method choose, as follows:

package org.springframework.cloud.client.loadbalancer;

import org.springframework.cloud.client.ServiceInstance;

public interface ServiceInstanceChooser {

    ServiceInstance choose(String serviceId);
}

From the two interfaces we can determine the initial load balancer should have four basic methods for the four basic methods, let's look at each:

  • chooseMethod: It is based on the incoming service ID, find the service instance corresponding to the load balancer.
  • executeMethods (a): using the load balancer according to the service IDselected content to perform the requested service instance.
  • executeThe method (second): the specified service instances to perform the requested content.
  • reconstructURIMethod: The original incoming service request instances and URIto construct a suitable host:portform URI. In a distributed service call, we use the service name instead of host:portthe form to initiate a call requesting service, such as: String result = restTemplate.getForEntity("http://producer-service/hello/producer", String.class).getBody();where are using the service name of the service provider producer-serviceto make the call, then this method is to run the service name in the form of calls for the replacement host:portform.

We have been emphasizing service instance, do service instance object is stored in the service side of the caller? Obviously not, we service the caller from Eureka Serverpulling a list of examples of services available, and the service list of examples is actually stored in the object services provider associated metadata, with a look ServiceInstanceSource:

package org.springframework.cloud.client;

import java.net.URI;
import java.util.Map;

public interface ServiceInstance {

	String getServiceId();

	String getHost();

	int getPort();

	boolean isSecure();

	URI getUri();

	Map<String, String> getMetadata();

	default String getScheme() {
		return null;
	}
}

It is an interface, we can start to achieve implementation class objects that interface to obtain the service ID, , HOST, PORT, URIother metadata and whether to enable HTTPSbasic information, from basic information, we can be assembled real call to address based on the service name , and it is noted.

By further reading the code, we are in the interface LoadBalancerClientpackage is located org.springframework.cloud.client.loadbalancerand found two important classes: LoadBalancerInterceptorand LoadBalancerAutoConfiguration, seen from the name, is a first class load balancer interceptor, the second is automated configuration class load balancer, which We can be known from the relationship between class diagram below:


We begin to see the LoadBalancerAutoConfigurationclass, to see if there could help us understand what code to achieve load balancing principle.

package org.springframework.cloud.client.loadbalancer;

import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.retry.backoff.BackOffPolicy;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.web.client.RestTemplate;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * Auto configuration for Ribbon (client side load balancing).
 *
 * @author Spencer Gibb
 * @author Dave Syer
 * @author Will Tran
 * @author Gang Li
 */
@Configuration
@ConditionalOnClass(RestTemplate.class)
@ConditionalOnBean(LoadBalancerClient.class)
@EnableConfigurationProperties(LoadBalancerRetryProperties.class)
public class LoadBalancerAutoConfiguration {

	@LoadBalanced
	@Autowired(required = false)
	private List<RestTemplate> restTemplates = Collections.emptyList();

	@Bean
	public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(
			final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {
		return () -> restTemplateCustomizers.ifAvailable(customizers -> {
            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);
		}

		@Bean
		@ConditionalOnMissingBean
		public RestTemplateCustomizer restTemplateCustomizer(
				final LoadBalancerInterceptor loadBalancerInterceptor) {
			return restTemplate -> {
                List<ClientHttpRequestInterceptor> list = new ArrayList<>(
                        restTemplate.getInterceptors());
                list.add(loadBalancerInterceptor);
                restTemplate.setInterceptors(list);
            };
		}
	}

	@Configuration
	@ConditionalOnClass(RetryTemplate.class)
	public static class RetryAutoConfiguration {

		@Bean
		@ConditionalOnMissingBean
		public LoadBalancedRetryFactory loadBalancedRetryFactory() {
			return new LoadBalancedRetryFactory() {};
		}
	}

	@Configuration
	@ConditionalOnClass(RetryTemplate.class)
	public static class RetryInterceptorAutoConfiguration {
		@Bean
		@ConditionalOnMissingBean
		public RetryLoadBalancerInterceptor ribbonInterceptor(
				LoadBalancerClient loadBalancerClient, LoadBalancerRetryProperties properties,
				LoadBalancerRequestFactory requestFactory,
				LoadBalancedRetryFactory loadBalancedRetryFactory) {
			return new RetryLoadBalancerInterceptor(loadBalancerClient, properties,
					requestFactory, loadBalancedRetryFactory);
		}

		@Bean
		@ConditionalOnMissingBean
		public RestTemplateCustomizer restTemplateCustomizer(
				final RetryLoadBalancerInterceptor loadBalancerInterceptor) {
			return restTemplate -> {
                List<ClientHttpRequestInterceptor> list = new ArrayList<>(
                        restTemplate.getInterceptors());
                list.add(loadBalancerInterceptor);
                restTemplate.setInterceptors(list);
            };
		}
	}
}

More dry goods share, welcome attention to my micro-channel public number: Java Mountain (Micro Signal: itlemon)
Here Insert Picture Description

Published 73 original articles · won praise 84 · views 470 000 +

Guess you like

Origin blog.csdn.net/Lammonpeter/article/details/85301058