In the previous article, Load Balancer Client was introduced to achieve load balancing. Here, Spring cloud ribbon is introduced.
1,ribbon
Spring Cloud Ribbon is a client-side load balancing tool based on Http and TCP, which is implemented on Netflix Ribbon. Unlike service registries, configuration centers and API gateways, Ribbon is not deployed independently, but it exists in almost every micro-service infrastructure. Understanding Ribbon is very important for us to use Spring Cloud, because load balancing is one of the most important means for high availability of the system, relieving network pressure and expanding processing capacity.
Internally, it provides an interface called ILoadBalance to represent the operations of load balancer, such as adding server operations, selecting server operations, getting all server lists, getting available server lists, and so on.
2. Here we copy the previous chapter of consumer project, modify pom.xml, and increase ribbon dependency
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <artifactId>eurekaRibbon</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>eurekaRibbon</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.4.0.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <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> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Dalston.SR3</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
3. Modify the main class to add RestTemplate @LoadBalanced annotation
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@EnableDiscoveryClient
@SpringBootApplication
public class EurekaRibbonApplication {
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(EurekaRibbonApplication.class, args);
}
}
4, interface
package com.example.demo.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
public class ClientRibbonController {
@Autowired
RestTemplate restTemplate;
@GetMapping("/consumer")
public String all() {
// Launching REST requests
return restTemplate.getForObject("http://eurekaClient/all", String.class);
}
}
The host location requested here is not in the form of a specific IP address and port, but in the form of a service name. Spring Cloud Ribbon has an interceptor, which can automatically select service instances when making actual calls here, and replace the service names here with the actual IP addresses and ports to be requested, thus completing service interface calls, thus realizing automation:
However, Ribbon's automated load balancing configuration needs to satisfy the following two conditions:
ConditionalOnClass(RestTemplate.class): RestTemplate class must exist in the current engineering environment
2. Conditional OnBean (Load Balancer Client. class): Use the @Load Balanced annotation to mark RestTemplate to configure RestTemplate using Load Balancer Client.
There are three main things to do in automated configuration:
- Create a LoadBalancer Interceptor Bean for intercepting requests from clients to achieve load balancing.
- Create a Bean for RestTemplate Customizer to add LoadBalancer Interceptor interceptor to RestTemplate.
- A list of EstTemplate objects modified by the @LoadBalanced annotation is maintained and initialized here. LoadBalancer Interceptor interceptors are added to EstTemplate that requires client load balancing by calling an instance of RestTemplate Customizer.
1. Conditional 2 Load Balancer Client
/** * Represents a client side load balancer * @author Spencer Gibb */ public interface LoadBalancerClient extends ServiceInstanceChooser { /** * execute request using a ServiceInstance from the LoadBalancer for the specified * service * @param serviceId the service id to look up the LoadBalancer * @param request allows implementations to execute pre and post actions such as * incrementing metrics * @return the result of the LoadBalancerRequest callback on the selected * ServiceInstance */ <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException; /** * execute request using a ServiceInstance from the LoadBalancer for the specified * service * @param serviceId the service id to look up the LoadBalancer * @param serviceInstance the service to execute the request to * @param request allows implementations to execute pre and post actions such as * incrementing metrics * @return the result of the LoadBalancerRequest callback on the selected * ServiceInstance */ <T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException; /** * Create a proper URI with a real host and port for systems to utilize. * Some systems use a URI with the logical serivce name as the host, * such as http://myservice/path/to/service. This will replace the * service name with the host:port from the ServiceInstance. * @param instance * @param original a URI with the host as a logical service name * @return a reconstructed URI */ URI reconstructURI(ServiceInstance instance, URI original); }
We can know that the select () method selects a corresponding service instance from the load balancer according to the incoming serviceId service Id. The execute() method executes the request content based on the serviceId service ID and request. The reconstruct URI () method constructs a suitable Host:Port URI. Ribbon Load Balancer Client is the concrete implementation of Load Balancer Client
2. Ribbon automation configuration: LoadBalancer AutoConfiguration
/** * Auto configuration for Ribbon (client side load balancing). * * @author Spencer Gibb * @author Dave Syer * @author Will Tran */ @Configuration @ConditionalOnClass(RestTemplate.class) //condition : RestTemplate Must be in the engineering classpath @ConditionalOnBean(LoadBalancerClient.class) //condition: Spring Containers must contain LoadBalancerClient Realization, namely RibbonLoadBalancerClient @EnableConfigurationProperties(LoadBalancerRetryProperties.class) //Start the retry function, you can spring.cloud.loadbalancer.retry=false,Cancel retry, default parameter is true public class LoadBalancerAutoConfiguration { @LoadBalanced @Autowired(required = false) private List<RestTemplate> restTemplates = Collections.emptyList(); //Maintain one RestTemplate List, through LoadBalanced To annotate. @Bean public SmartInitializingSingleton loadBalancedRestTemplateInitializer( //Loading Initial Words Customized restTeplate,Essentially initialization InterceptingHttpAccessor Specific call 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); } @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 new RestTemplateCustomizer() { @Override public void customize(RestTemplate restTemplate) { List<ClientHttpRequestInterceptor> list = new ArrayList<>( restTemplate.getInterceptors()); list.add(loadBalancerInterceptor); restTemplate.setInterceptors(list); } }; } } @Configuration @ConditionalOnClass(RetryTemplate.class) static class RetryAutoConfiguration { @Bean public RetryTemplate retryTemplate() { RetryTemplate template = new RetryTemplate(); template.setThrowLastExceptionOnExhausted(true); return template; } @Bean @ConditionalOnMissingBean public LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory() { return new LoadBalancedRetryPolicyFactory.NeverRetryFactory(); } @Bean public RetryLoadBalancerInterceptor ribbonInterceptor( LoadBalancerClient loadBalancerClient, LoadBalancerRetryProperties properties, LoadBalancedRetryPolicyFactory lbRetryPolicyFactory, LoadBalancerRequestFactory requestFactory) { return new RetryLoadBalancerInterceptor(loadBalancerClient, retryTemplate(), properties, lbRetryPolicyFactory, requestFactory); } @Bean @ConditionalOnMissingBean public RestTemplateCustomizer restTemplateCustomizer( //custom RestTemplate ,Essentially initialization InterceptingHttpAccessor final RetryLoadBalancerInterceptor loadBalancerInterceptor) { return new RestTemplateCustomizer() { @Override public void customize(RestTemplate restTemplate) { List<ClientHttpRequestInterceptor> list = new ArrayList<>( restTemplate.getInterceptors()); list.add(loadBalancerInterceptor); restTemplate.setInterceptors(list); } }; } } }
You can see it in detail. https://www.cnblogs.com/duanxz/p/7504947.html Source code introduction