This is the 15th day of my participation in Gwen Challenge

One, foreword

Start with a demo, then analyze the ribbon flow, and finally write down the source code.


(1)Ribbon + RestTemplatecase

A brief introduction to the implementation principle:

  • Through theRestTemplateTo add@LoadBalancedAdding interceptors
  • Interceptor throughRibbonSelecting a service instance
  • Then replace the service name in the request address withRibbonOf the selected service instanceIPAnd port
  1. pom.xmlThe configuration file
<! 'eureka-client' in 'SpringCloud' -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    <version>2.1.2. RELEASE</version>
</dependency>
<! -- Directly import 'ribbon' -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
Copy the code
  1. RestTemplateThe use of
@Configuration
public class RestTemplateConfig {

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(a) {

        return newRestTemplate(); }}Copy the code


(2) DiagramRibbonprocess

The general process is shown as follows:

The general process can be divided into:

  1. Note: automatic assemblyLoadBalancerAutoConfiguration
  2. In the auto-configuration class, isRestTemplateAdding interceptorsLoadBalancerInterceptor
  3. Gets in the interceptor after invoking the requesthostAnd, inLoadBalancerClientIn thehostThe information is transformed to get the real server address.
  4. LoadBalancerClientIn the fromEureka clientGet a list of service instances and then include load balancing rulesIRule, which is selected to initiate the callserver.
  5. To be responsible forHTTPComponent of communicationLoadBalancerRequestExecute trueHTTPThe request.





Initialize the source code

It is mainly divided into:

  1. Initialization: annotations, autowage, adding interceptors
  2. Request initialization
  3. spring-cloudribbonDefault for integrationILoadBalancer

1) Initialization: annotations, auto-assembly, adding interceptors

This process is mainly divided into three parts:

  1. from@LoadBalancedAnnotation of
  2. LoadBalancerAutoConfigurationAutomatically.
  3. RestTemplateSetting up interceptors


The flow chart is as follows:


Detailed process source code is as follows:

  1. From the first@LoadBalancedStart with notes:

On the RestTemplate, annotate @loadBalance.

/ / positioning: org. Springframework. Cloud. Client. The loadbalancer
@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface LoadBalanced {
}
Copy the code
  1. LoadBalancerAutoConfigurationAutomatically.

For Spring Boot and Spring Cloud classes, look directly for XXXAutoConfiguration:

/ / positioning: org. Springframework. Cloud. Client. The loadbalancer
@Configuration
@ConditionalOnClass(RestTemplate.class) // The class is loaded when it exists
@ConditionalOnBean(LoadBalancerClient.class)
@EnableConfigurationProperties(LoadBalancerRetryProperties.class)
public class LoadBalancerAutoConfiguration {
    
    // Put the user-created 'RestTemplate' here
    @LoadBalanced
    @Autowired(required = false)
    privateList<RestTemplate> restTemplates = Collections.emptyList(); . .// Use 'RestTemplateCustomizer' to customize each 'RestTemplate'
    // Set each 'RestTemplate' to the 'Interceptor'
    @Bean
    public SmartInitializingSingleton loadBalancedRestTemplateInitializer(
                    final List<RestTemplateCustomizer> customizers) {
            return new SmartInitializingSingleton() {
                    @Override
                    public void afterSingletonsInstantiated(a) {
                            for (RestTemplate restTemplate : 
                 LoadBalancerAutoConfiguration.this.restTemplates) {
                                    for(RestTemplateCustomizer customizer : customizers) { customizer.customize(restTemplate); }}}}; }... .// 3. Add interceptor to RestTemplate
    @Configuration
    @ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
    static class LoadBalancerInterceptorConfig {... ./ / create RestTemplateCustomizer
    @Bean
    @ConditionalOnMissingBean
    public RestTemplateCustomizer restTemplateCustomizer(
                    final LoadBalancerInterceptor loadBalancerInterceptor) {
            return new RestTemplateCustomizer() {
                    @Override
                    public void customize(RestTemplate restTemplate) {
        // Add interceptors to each RestTemplate
        List<ClientHttpRequestInterceptor> list = new ArrayList<>(
                                            restTemplate.getInterceptors());
        list.add(loadBalancerInterceptor);
        // The interceptor is customized with RestTemplateCustomizerrestTemplate.setInterceptors(list); }}; }}}Copy the code

A little wayLoadBalancerInterceptor:

  • By calling the RestTemplateCustomizer. Customize () method to add interceptors to RestTemplate LoadBalancerInterceptor.

  • Load balancing is implemented in the LoadBalancerInterceptor.


2) Request initialization

The main process is divided into:

  1. Interceptor handling: address translation

  2. LoadBalancerClient process: Implements load balancing

  3. Interceptor handling: address translation

The general process is shown as follows:

RestTemplate. GetForObject (” http://serviceA/hello “) into: restTemplate. GetForObject (” http://172.18.1.24:8080/hello “).

/ / positioning: org. Springframework. Cloud. Client. The loadbalancer
public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {
    private LoadBalancerClient loadBalancer;
	privateLoadBalancerRequestFactory requestFactory; . .// Replace host here with the actual IP :port
	@Override
	public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
			final ClientHttpRequestExecution execution) throws IOException {
		final URI originalUri = request.getURI();
		String serviceName = originalUri.getHost();
		
        // Hand it over to LoadBalancerClient to handle the real HTTP request
        // The real IRule implementation logic will be performed, using load balancing rules to filter out suitable service instances
		return this.loadBalancer .execute(serviceName, requestFactory.createRequest(request, body, execution)); }}Copy the code


  1. LoadBalancerClientProcessing to achieve load balancing

    The interceptor is really just a simple wrapper, handing the request directly to the LoadBalancerClient to execute the item’s request.

public class RibbonLoadBalancerClient implements LoadBalancerClient {

	privateSpringClientFactory clientFactory; . .@Override
	public <T> T execute(String serviceId, LoadBalancerRequest<T> request) 
        throws IOException {
        // Find the load balancer corresponding to the service
		ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
        
        / / is called loadBalancer. ChooseServer method
        // The server already contains a specific IP address and portServer server = getServer(loadBalancer); . .// Execute the real HTTP request
		returnexecute(serviceId, ribbonServer, request); }... . }Copy the code

Then thisRibbonLoadBalancerClientWhen was it initialized?

Find RibbonAutoConfiguration

/ / location: org.springframework.cloud.net flix. Ribbon
@Configuration
@ConditionalOnClass({ IClient.class, RestTemplate.class, AsyncRestTemplate.class, Ribbon.class})
@RibbonClients
/ / to perform after EurekaClientAutoConfiguration, namely eureka - client executed after initialization is complete.
@AutoConfigureAfter( name = "org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration")
/ / before LoadBalancerAutoConfiguration execution
@AutoConfigureBefore({LoadBalancerAutoConfiguration.class, AsyncLoadBalancerAutoConfiguration.class})
@EnableConfigurationProperties({RibbonEagerLoadProperties.class, ServerIntrospectorProperties.class})
public class RibbonAutoConfiguration {... . }Copy the code

In addition,ribbonPackage structure, as follows:

  1. Ribbon - 2.2.5. Jar:ribbonSome of the core components
  2. Ribbon - transport - 2.2.5. JarBased on:nettyA particularly low-level process of encapsulationhttp,tcp,udpComponents of network communication for various protocols
  3. Ribbon - core - 2.2.5. Jar:ribbonBasic general code components
  4. Ribbon httpclient -- 2.2.5. JarIs:ribbonAt the bottom of thehttpSome components of network communication
  5. Ribbon loadbalancer -- 2.2.5. JarAre:ribbonThe core of the originalAPI