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.