This article introduces the Ribbon in SpringCloud. The Ribbon is the Ribbon that supports the Ribbon. The Ribbon is the Ribbon that supports the Ribbon. First, let’s take a look at what the Ribbon does when it launches.
I. Project case preparation
First of all, we use the RestTemplate to implement service invocation and the Ribbon to implement client load balancing. 463257262【QQ total group 】
1. The Order service
Our Order service acts as a service provider. Create a SpringBoot project and add dependencies
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.9</version>
<relativePath/> <! -- lookup parent from repository -->
</parent>
<groupId>com.bobo.springcloud</groupId>
<artifactId>spring-cloud-order-server</artifactId>
<version>0.0.1 - the SNAPSHOT</version>
<name>spring-cloud-order-server</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<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>Hoxton.SR10</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>
Copy the code
Then add the relevant configuration to the properties file
spring.application.name=spring-cloud-order-service
server.port=8081
Copy the code
Then create a custom Controller to provide external services
@RestController
public class OrderController {
@Value("${server.port}")
private int port;
@GetMapping("/orders")
public String orders(a){
System.out.println("Order service port is:"+port);
return "Order Services ..... "; }}Copy the code
We can then start the two Order services separately, with ports 8081 and 8082
2. The User service
The User service acts as the client that invokes the Order service. This is where we highlight Ribbon’s services. Also create a SpringBoot project and add related dependencies
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.9. RELEASE</version>
<relativePath/> <! -- lookup parent from repository -->
</parent>
<groupId>com.bobo.springcloud</groupId>
<artifactId>spring-cloud-user-service2</artifactId>
<version>0.0.1 - the SNAPSHOT</version>
<name>spring-cloud-user-service2</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR10</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</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>${spring-cloud.version}</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>
Copy the code
Then configure the information in the properties file
spring.application.name=spring-cloud-user-service
spring-cloud-order-service.ribbon.listOfServers=localhost:8081,localhost:8082
Copy the code
A custom Controller is then created to implement the service invocation
@RestController
public class UserController {
@Autowired
public RestTemplate restTemplate;
@Autowired
LoadBalancerClient loadBalancerClient;
@Bean
@LoadBalanced
public RestTemplate restTemplate(a){
return new RestTemplate();
}
@GetMapping("/users")
public String users(a){
ServiceInstance choose = loadBalancerClient.choose("spring-cloud-order-service");
String url = String.format("http://%s:%s",choose.getHost(),choose.getPort()+"/orders");
//return restTemplate.getForObject(url,String.class);
return restTemplate.getForObject("http://spring-cloud-order-service/orders",String.class); }}Copy the code
Then the User service is enabled. You can see that the Ribbon uses polling to invoke the service by default
Ii. Principle analysis of Ribbon
It’s easy to use. Let’s take a look at the core principles of the Ribbon. First, let’s look at what the Ribbon does.
1.RibbonAutoConfiguration
The Ribbon sets up Settings when the system starts up. Let’s start with the spring.Factories configurationemsp; So we will continue to look at the RibbonAutoConfiguration. We post the key information of [RibbonAutoConfiguration]
@Configuration
@Conditional({RibbonAutoConfiguration.RibbonClassesConditions.class})
@RibbonClients
@AutoConfigureAfter( name = {"org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration"} )
/ / RibbonAutoConfiguration configuration will be completed after injection container LoadBalancerAutoConfiguration and AsyncLoadBalancerAutoConfiguration injection
@AutoConfigureBefore({LoadBalancerAutoConfiguration.class, AsyncLoadBalancerAutoConfiguration.class})
@EnableConfigurationProperties({RibbonEagerLoadProperties.class, ServerIntrospectorProperties.class})
public class RibbonAutoConfiguration {
/** * If there is no LoadBalancerClient object in the IoC container, inject a *. The injected type is RibbonLoadBalancerClient object **/
@Bean
@ConditionalOnMissingBean({LoadBalancerClient.class})
public LoadBalancerClient loadBalancerClient(a) {
return new RibbonLoadBalancerClient(this.springClientFactory());
}
// omit other code
Copy the code
By checking the source code, we know that the injection of [LoadBalancerClient] object is completed when the SpringBoot project is started, and the specific type is [RibbonLoadBalancerClient]. At the same time also will complete this configuration LoadBalancerAutoConfiguration 】 【 type of loading. Before watching [LoadBalancerAutoConfiguration] does something, we first come to clear annotations @ LoadBalanced 】 【 role
2.LoadBalancerAutoConfiguration
@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface LoadBalanced {
}
Copy the code
@loadBalanced is essentially an @qualifier annotation. The role is to tag, and we’ll demonstrate that through a case study.
Define a simple [User] class
public class User {
String name;
public User(String name) {
this.name = name;
}
public String getName(a) {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString(a) {
return "User{" +
"name='" + name + '\' ' +
'} '; }}Copy the code
Then define a Java configuration class with the @loadBalanced annotation added to two and one not.
@Configuration
public class JavaConfig {
@LoadBalanced
@Bean("user1")
public User user1(a){
return new User("user1");
}
@Bean("user2")
public User user2(a){
return new User("user2");
}
@LoadBalanced
@Bean("user3")
public User user3(a){
return new User("user3"); }}Copy the code
Then create our controller to test use
@RestController
public class UsersController {
@LoadBalanced
@Autowired
List<User> list = Collections.emptyList();
@GetMapping("/querys")
public String query(a){
returnlist.toString(); }}Copy the code
The project structure
Let’s see what happens when we start the SpringBoot project
Understand the action of the [@ LoadBalanced], we’ll look at the configuration of the loading LoadBalancerAutoConfiguration 】 【 what do
public class LoadBalancerAutoConfiguration {
/** * 1.@LoadBalancedAnnotation modifies RestTemplate objects * These objects are stored in a collection **/
@LoadBalanced
@Autowired(required = false)
private List<RestTemplate> restTemplates = Collections.emptyList();
@Autowired(required = false)
private List<LoadBalancerRequestTransformer> transformers = Collections.emptyList();
4 / * * * * SmartInitializingSingleton object is injected into the container, And implements SmartInitializingSingleton * afterSingletonsInstantiated method declaration in the interface, This method implements the implantation of the RestTemplate object interceptor **/ through the Customize method * defined in RestTemplateCustomizer in 3
@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); }}}); }@Bean
@ConditionalOnMissingBean
public LoadBalancerRequestFactory loadBalancerRequestFactory( LoadBalancerClient loadBalancerClient) {
return new LoadBalancerRequestFactory(loadBalancerClient, this.transformers);
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
static class LoadBalancerInterceptorConfig {
/** * create a LoadBalancerInterceptor and inject it into the container **/
@Bean
public LoadBalancerInterceptor loadBalancerInterceptor( LoadBalancerClient loadBalancerClient, LoadBalancerRequestFactory requestFactory) {
return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
}
/** * 3. * Creates a RestTemplateCustomizer and injects it into the container * and defines the logic **/ that defines the Customize method in the RestTemplateCustomizer interface via an inner class
@Bean
@ConditionalOnMissingBean
public RestTemplateCustomizer restTemplateCustomizer(
final LoadBalancerInterceptor loadBalancerInterceptor) {
return restTemplate -> {
// Get the original interceptor of the RestTemplate
List<ClientHttpRequestInterceptor> list = new ArrayList<>(
restTemplate.getInterceptors());
// Add a LoadBalancerInterceptor to the original interceptor
list.add(loadBalancerInterceptor);
// Then set the collection with the new interceptor to the RestTemplate objectrestTemplate.setInterceptors(list); }; }}// omit other code
}
Copy the code
This configuration class implements the implantation of the RestTemplate object (modified by @loadBalanced)
LoadBalancerInterceptor The function of the interceptor.
Summary of operations in the Ribbon system
Now that you know the logic behind the RestTemplate plugin, the Ribbon explains how the Ribbon handles load balancing logic.