Spring Cloud Cognitive Learning: Ribbon Use

Catalog

(viii)Connect Last Spring Cloud Cognitive Learning (1): Introduction to Spring Cloud and Eureka Use
(viii)The previous article introduced Spring Cloud and the simple use of Eureka, a component of service registration and discovery.
(viii)In this article, we describe how Ribbon can be used to solve load balancing problems for multiple services once they are built.

Ribbon Load Balancing

What does client-side load balancing mean, let clients do load balancing, not service-side load balancing?For example, if you are going to queue for shopping, there are three queues, and you naturally choose a short queue to queue up_This is the load balancing that you go to, not which queue the attendant will help you arrange for you.
(viii)Why use client load balancing?

  • Mainly, a balanced load on the client side will reduce the resource consumption on the server side, as if there were many consumers, you would either hire a lot of servers, or you would leave consumers and other servers free.
  • Secondly, this is also an architectural consideration, because clients pull the list of available services from eureka, and if you pull the load status of various services at this time in passing, you can use this information to load balance clients.


Simple steps to use:

The following code can be referenced: Simple Ribbon Load Balancing Experiment

1. New module for load balancing

New module spring-cloud-user-service-8002, spring-cloud-user-service-8003


2. Modify the module code:

  • Import pom.xml, modify application.yml, and modify the main boot class for module spring-cloud-user-service-8002, spring-cloud-user-service-8003.
  • The code is basically the same as the module spring-cloud-user-service-8001.
  • pom.xml is the same as spring-cloud-user-service-8001.
  • Controllers, mapper s, are the same as spring-cloud-user-service-8001.Only the name of the main program class
  • Mainly modify the service port and database.

Since a new service provider was created, in order for different services to use different databases, execute the following SQL to create additional databases:

sql:

-- db2
DROP DATABASE IF EXISTS cloud02;
CREATE DATABASE cloud02 CHARACTER SET UTF8;
USE cloud03;
CREATE TABLE user
(
  id int PRIMARY KEY AUTO_INCREMENT,
  username VARCHAR(255),
  fullName  VARCHAR(255)
);


INSERT INTO user(username,fullName) VALUES('zhangsan','Zhang San 2');
INSERT INTO user(username,fullName) VALUES('lisi','Li Si 2');
INSERT INTO user(username,fullName) VALUES('wangwu','Wang Wu2');
INSERT INTO user(username,fullName) VALUES('zhaoliu','Zhao Liu2');
INSERT INTO user(username,fullName) VALUES('lidazhuang','Li Daizhuang 2');

 
SELECT * FROM user;

--- db3
DROP DATABASE IF EXISTS cloud03;
CREATE DATABASE cloud03 CHARACTER SET UTF8;
USE cloud03;
CREATE TABLE user
(
  id int PRIMARY KEY AUTO_INCREMENT,
  username VARCHAR(255),
  fullName  VARCHAR(255)
);


INSERT INTO user(username,fullName) VALUES('zhangsan','Zhang San 3');
INSERT INTO user(username,fullName) VALUES('lisi','Li Si 3');
INSERT INTO user(username,fullName) VALUES('wangwu','Wang Wu3');
INSERT INTO user(username,fullName) VALUES('zhaoliu','Zhao Liu3');
INSERT INTO user(username,fullName) VALUES('lidazhuang','Li Daizhuang 3');

 
SELECT * FROM user;


3. Start module

3.1 Start service producers:
spring-cloud-eureka-server-7001,spring-cloud-user-service-8001,spring-cloud-user-service-8002,spring-cloud-user-service-8003
Looking at how many service instances are registered internally in eureka, you can see that all three service instances we started are displayed.

3.2 Start service consumers:
Now there are three service instances for the USERSERIVE service, and then we start the Service Consumer module to see how he invoked it after several calls.
Since there were minor modifications to the databases of the different services above (to distinguish between them, I modified the data a bit, but they should be the same in the actual business), you can use the data to determine which database is currently being called.
Calling http://localhost/user/list multiple times, you should see the data changing, indicating that there is load balancing by default.


_You may be a little confused, when is the default load balancing configured?Remember what we configured before when we configured consumers to get lists of services from eureka?We added a comment @LoadBalanced to our restTemplate, which means load balancing.

When you invoke a service, you will see that consumers pull a list of services and get health information for some services.

By default, services pulled from eureka use polling calls (joining three ABC service instances makes one-by-one calls sequentially, such as possibly in the BCA sequence); but ribbon can help us do more.

Below, Ribbon is used for load balancing on the client side.



4. Modify the consumer module

Since Ribbon is client load balanced, modify the consumer module spring-cloud-user-consumer-80:
4.1 Modify pom.xml to add ribbon dependencies:

        <!--increase ribbon rely on start-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
            <!--Old version needs to be removed-netflix-->
        </dependency>
        <!--increase ribbon rely on end-->

4.2. Modify the load balancing policy:
_When importing ribbon s, the default load balancing or polling is done if you don't do anything else.Let's modify the load balancing strategy below:

@Configuration
public class AppConfig {
    @Bean
    @LoadBalanced // eureka works with this to use LoadBalanced to invoke services registered in eureka
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

    @Bean
    public IRule myRule() {
//        return new RoundRobinRule();
		return  new RandomRule();
//        return new RetryRule();
    }
}

Then you call it again a few timesHttp://localhost/user/listYou should see that the load balancing strategy has changed.

Modifying the load balancing policy is demonstrated in the example above.Next, it's about balancing things.



Load balancing algorithm:

IRule is responsible for the load balancing strategy in Ribbon, so the code above creates a new IRule bean.
Here are a few common implementation classes for this IRule.

  • RandomRule: Random call service
  • RoundRobinRule: Polling calls to services
  • WeightedResponseTimeRule: A weighting policy that calls more times when a service has a higher weight.A weight table is maintained internally, and the weights are updated every once in a while based on the response time of the service. The weights with shorter response time are higher.(Polling policy is used first at startup because there is no information about service calls.)
  • RetryRule: The default internal is a polling call (note that this policy can be changed), but it is a polling with retry mechanism, ABC three service instances. If the B service instance suddenly hangs up, the default polling policy should fail when polling to B. If RetryRule is used, B will try to call C when it cannot be called to ensure that this call isSuccessful.
@Configuration
public class AppConfig {
    @Bean
    @LoadBalanced // eureka works with this to use LoadBalanced to invoke services registered in eureka
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

    @Bean
    public IRule myRule(){
//        return new RandomRule(); //Random call service
//        Return new RoundRobinRule ();//Polling Policy
        return new RetryRule();// Polling with Retry
    }
}

Custom load rules:

In addition to the rules above, you can also customize the rules for load balancing.
You might think, then I should be able to rewrite it with reference to the implementation of RandomRule or RoundRobinRule, and specify the implementation object of IRule as above.
(viii)However, it is important to note that if you are in the same or subordinate directory of the main program class (that is, the directory scanned by the ComponentScan of the main program class), this rule will take effect for all services invoked by this consumer.
1. Write a load balancing rule: Call only 8003 services.

package com.progor.study.myrule;

import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;

import java.util.List;

public class MyLoadBalancedRule extends AbstractLoadBalancerRule {

    // This choose method is to select which service to invoke
    public Server choose(ILoadBalancer lb, Object key) {
        // ILoadBalancer is a list of service registrations
        if (lb == null) {
            return null;
        }
        Server server = null;

        while (server == null) {
            if (Thread.interrupted()) {
                return null;
            }
            List<Server> upList = lb.getReachableServers();//Available Services
            List<Server> allList = lb.getAllServers();// All Services

            int serverCount = allList.size();
            if (serverCount == 0) {
                // Number of services is 0
                return null;
            }

            // Looking at the code below (this code is from RandomRule), you should be able to tell that the returned server is specified here.
            // So we're also here to modify the policy if we specify that only 8003 will be called
//            int index = rand.nextInt(serverCount);
//            server = upList.get(index);
            // Modify start
            if (upList.size()==0){
                return null;
            }
            for (int i = 0; i < upList.size() ; i++) {
                Server item = upList.get(i);
                int httpPort = item.getPort();
                // You can customize your rules with various parameters of Server and ILoadBalancer ()
                if (httpPort == 8003){
                    server = item; // Since this is just an example, it is written casually, so the security logic is not complete.
                }
            }
            // Modify end

            if (server == null) {
                Thread.yield();
                continue;
            }

            if (server.isAlive()) {
                return (server);
            }

            server = null;
            Thread.yield();
        }

        return server;

    }

    @Override
    public Server choose(Object key) {
        return choose(getLoadBalancer(), key);
    }

    @Override
    public void initWithNiwsConfig(IClientConfig clientConfig) {
        // TODO Auto-generated method stub

    }
}

2. Create a Configuration in the external directory of the main program class:

package com.progor.config;

import com.netflix.loadbalancer.IRule;
import com.progor.study.myrule.MyLoadBalancedRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MyConfig {

    @Bean
    public IRule myRule(){
        return new MyLoadBalancedRule();// Use our custom rules
    }
}

3. Modify the main program class and add @RibbonClient
If your Configuration is placed outside the main program class, add this to scan outside Configuration:
(viii)This annotation is used to specify a load balancing rule for a service. If you do not use this annotation to configure a load balancing policy for the corresponding service, and you specify an instance of IRule in the configuration class of the inner directory as an instance of the implementation class that we created, then all services will adopt this policy.

package com.progor.study;

import com.progor.config.MyConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.ribbon.RibbonClient;

@SpringBootApplication
@EnableEurekaClient
@RibbonClient(name="USERSERIVE",configuration= MyConfig.class)
public class UserConsumer80Application {
    public static void main(String[] args) {
        SpringApplication.run(UserConsumer80Application.class, args);
    }
}


4. CallHttp://localhost/user/listYou will find that only 8003 data are returned, which indicates that our load balancing is in effect.


_For issues with load balancing rules for different services, you can also refer to the MessageService in your code.Here I wrote two service instances of a Message service, 8004 and 8005, and asked consumers to invoke the service as follows:

  • When not using @RibbonClient: Since that Configuration was not scanned, it will not take effect and all services should use the internally configured load balancing rules
    If internal definition rules are used in the internal configuration:
    @Bean
    public IRule myRule(){s
//        return new RandomRule(); //Random call service
//        Return new RoundRobinRule ();//Polling Policy
        return new RetryRule();// Polling with Retry
    }

When configuring the amount of the rule internally, if you use the rule we defined: Since we previously defined a rule for USERSERVICE to use only 8003 services, MESSAGESERVICE will also use this rule, then MESSAGESERVICE will never be able to request it.

    @Bean
    public IRule myRule() {
//		return  new RoundRobinRule();
//		return  new RandomRule();
//        return new RetryRule();
        return new MyLoadBalancedRule();
    }

  • When using @RibbonClient: For the specified service, the specified load balancing rule is used.[From other data (indeterminate)_) I understand that the previous @RibbonClient name should be empty and that there is a default default default from the annotated name, which I think should be correct.But this is not allowed, it should be to avoid the problem of exclusive load balancing rules overriding global rules.)
//  Code for custom load balancing
package com.progor.config;

import com.netflix.loadbalancer.IRule;
import com.progor.study.myrule.MyLoadBalancedRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MyConfig {

    @Bean
    public IRule myRule() {
        return new MyLoadBalancedRule();// Use our custom rules
    }
}

//  Main Program Class Code
@SpringBootApplication
@EnableEurekaClient
@RibbonClient(name = "USERSERIVE", configuration = MyConfig.class) 
public class UserConsumer80Application {
    public static void main(String[] args) {
        SpringApplication.run(UserConsumer80Application.class, args);
    }
}

Tags: Java Spring Database xml SQL

Posted on Thu, 14 May 2020 21:34:05 -0700 by kool_samule