Client load balancing Ribbon

Keywords: Maven Nginx REST Spring Cloud Microservices

What is the Ribbon in Spring Cloud?

We usually say that load balancing means that a request is evenly distributed to different node units for execution. Load balancing is divided into hardware load balancing and software load balancing;

Hardware load balancing: such as F5, Shenxin, Array, etc;

Software load balancing: such as Nginx, LVS, HAProxy, etc;

For hardware load balancing or software load balancing, they will maintain an available server list and eliminate the failed server nodes through heartbeat detection to ensure that all the server nodes in the list can be accessed normally. When the client sends a request to the load balancing device, the device takes the address of a server from the maintained list of available servers according to some algorithm (such as polling, weight, minimum number of connections, etc.), and then forwards it.

Ribbon is an open source project released by Netflix. Its main function is to provide software load balancing algorithms for clients. It is a client load balancing tool based on HTTP and TCP.

Spring Cloud encapsulates the Ribbon twice, which allows us to use the service request of RestTemplate to automatically convert it into the service call of client load balancing.

Ribbon supports a variety of load balancing algorithms and custom load balancing algorithms.

Ribbon is just a tool class framework, which is relatively small. Spring Cloud is also very convenient to use after it is encapsulated. Unlike service registry, configuration center and API gateway, it needs to be deployed independently. Ribbon only needs to be used directly in code;

The difference between Ribbon and Nginx:

Ribbon is a client-side load balancing tool. The biggest difference between client-side load balancing and server-side load balancing is that the service lists are stored in different locations. In client-side load balancing, the service lists under all client nodes need to be obtained from the service registry, such as Eureka service registry. Similar to the server-side load balancing architecture, the client-side load balancing also needs a heartbeat to maintain the health of the server-side list, but this step needs to be completed in cooperation with the service registry. In Spring Cloud, because Spring Cloud encapsulates the ribbon twice, an automatic integration configuration for the ribbon will be created by default.

In Spring Cloud, the Ribbon is mainly used with the RestTemplate object configuration. The Ribbon will automatically configure the RestTemplate object and enable load balancing when the RestTemplate object is called through @ LoadBalanced.

Ribbon enables client load balancing

Due to the encapsulation of Spring Cloud Ribbon, it is very simple for us to use client-side load balancing call in microservice architecture. We only need the following two steps:

1. Start multiple service provider instances and register with a service registry or service registry cluster.

2. The service consumer invokes the service provider through the RestTemplate modified by the @ LoadBalanced annotation.

In this way, we can achieve high availability of service providers and load balancing calls of service consumers.

Ribbon load balancing strategy

The load balancing strategy of Ribbon is defined by IRule interface, which is implemented as follows:

Random rule: random policy;

Roundrobin rule: polling policy (default);

WeightResponseTimeRule: calculate the weight of all services according to the average response time. The faster the response time, the greater the service weight and the higher the probability of being selected. If the statistical information is insufficient when the service is just started, use the roundrobin rule policy. When the statistical information is sufficient, switch to the WeightResponseTimeRule policy;

RetryRule: first distribute according to the roundrobin rule policy. If the distributed service cannot be accessed, retry within the specified time to distribute other available services;

Best available rule: first filter out the services with multiple access failures, and then select a service with the least concurrency;

ZoneAvoidanceRule: comprehensively judge the performance of the region where the service node is located and the availability of the service node to decide which service to choose.

If you want to adopt other load balancing strategies, write the following in the configuration class of the calling service:

@Configuration  //Equivalent to spring applicationContext.xml configuration file
public class BeanConfig {
    /*
    * @Bean Equivalent to:
    * <bean id="restTemplate" class="xxx.xxx.xxx.RestTemplate">
    * </bean>
    * */
    @LoadBalanced  //Call for load balancing using Ribbon
    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }

    /**
     * Overwrite the original Ribbon's default polling load balancing policy
     * @return
     */
    @Bean
    public IRule iRule(){
        return new RandomRule();  //Random load balancing strategy is adopted
        return new RetryRule();   //Load balancing strategy with retry
    }
}

Interpretation of Rest request template class

When we call the service of the service provider from the service consumer, we use an extremely convenient object called RestTemplate. At that time, we only used the simplest of the RestTemplate to enable getForEntity to initiate a get request to call the data of the service. At the same time, we also enable client load balancing by configuring the @ LoadBalanced annotation, RestTemplate is very powerful. Let's take a detailed look at the use of several common request methods in RestTemplate.

In daily operations, there are usually four Rest based methods, which are:

GET request -- query data;

POST request -- add data;

PUT request -- modify data;

DELETE request -- DELETE data;

GET request for RestTemplate

There are two ways to Get a request:

First: getForEntity

This method returns a responseentity < T > object. Responseentity < T > is Spring's encapsulation of HTTP request response, including several important elements, such as response code, contentType, contentLength, response message body, etc;

ResponseEntity<String> responseEntity = restTemplate.getForEntity("http://01-SPRINGCLOUD-SERVICE-PROVIDER/service/hello",String.class);
String body = responseEntity.getBody();
HttpStatus httpStatus = responseEntity.getStatusCode();
int statusCodeValue = responseEntity.getStatusCodeValue();
HttpHeaders headers = responseEntity.getHeaders();

Above code:

The first parameter of getForEntity method is the address of the service to be called, that is, the address provided by the service provider http://01-SPRINGCLOUD-SERVICE-PROVIDER/service/hello Interface address. Note that it is called through the service name instead of the service address. If it is changed to the service address, the Ribbon cannot be used to realize client load balancing;

The second parameter of getForEntity, String.class, indicates that the body type you want to return is String type. If you want to return an object, it is also possible, such as User object;

The second: getForObject()

The use of getForEntity is similar to that of getForEntity, except that getforebject is encapsulated again on the basis of getForEntity. The body information of http response body can be converted into a specified object to facilitate our code development;

This is more convenient when you do not need to return some information in the response, but only the body information;

User user = restTemplate.getForObject("http://01-SPRINGCLOUD-SERVICE-PROVIDER/service/getUser?id={0}&name={1}&phone={2}", User.class,strArray);

Returns a User object directly.

POST request for RestTemplate

post is very similar to get requests:

restTemplate.postForEntity();
restTemplate.postForObject();
restTemplate.postForLocation();

Example: add a method to the called service:

// @RequestMapping(value="/service/addUser",method = RequestMethod.POST)
 @PostMapping("/service/addUser")
 public User addUser(@RequestParam("id") Integer id,
                     @RequestParam("name") String name,
                     @RequestParam("phone") String phone){
     //Conduct business processing (omitted)
     System.out.println("service provider 1...");
     User user = new User();
     user.setId(id);
     user.setName(name);
     user.setPhone(phone);
     //Insert the user object into the database (omitted temporarily)
     return user;
 }

Test the post request method in the service consumer:

/**
     * The consumer calls the addUser method of the service provider
     * @return
     */
    @RequestMapping("/web/addUser")
    public User addUser(){
        // Logical judgment omits no writing
        // Call the method of the post request. The parameters are not spliced after the url address, and the parameters are passed through the request body
        // The form information and parameter data to be transferred (deceptive) cannot be hashmap
        MultiValueMap<String,Object> dataMap = new LinkedMultiValueMap<String,Object>();
        dataMap.add("id","1028");
        dataMap.add("name","chapter");
        dataMap.add("phone","1213141414234");

        ResponseEntity<User> responseEntity = restTemplate.postForEntity("http://01-SPRINGCLOUD-SERVICE-PROVIDER/service/addUser", dataMap, User.class);
        User body = responseEntity.getBody();
        HttpStatus httpStatus = responseEntity.getStatusCode();
        int statusCodeValue = responseEntity.getStatusCodeValue();
        HttpHeaders headers = responseEntity.getHeaders();
        System.out.println(body.getId()+"--"+body.getName()+"--"+body.getPhone());
        System.out.println(httpStatus);
        System.out.println(statusCodeValue);
        System.out.println(headers);

        User user = restTemplate.postForObject("http://01-SPRINGCLOUD-SERVICE-PROVIDER/service/addUser", dataMap, User.class);
        System.out.println(user.getId()+"--"+user.getName()+"--"+user.getPhone());
        
        return restTemplate.postForEntity("http://01-SPRINGCLOUD-SERVICE-PROVIDER/service/addUser",dataMap,User.class).getBody();
    }

PUT request for RestTemplate

restTemplate.put();

Example: add a method to the service provider:

//   @RequestMapping(value = "/service/updateUser",method = RequestMethod.PUT)
   @PutMapping("/service/updateUser")
   public User updateUser(@RequestParam("id") Integer id,
                       @RequestParam("name") String name,
                       @RequestParam("phone") String phone){
       //Conduct business processing (omitted)
       System.out.println("service provider 1...");
       User user = new User();
       user.setId(id);
       user.setName(name);
       user.setPhone(phone);
       //Modify database data (temporarily omitted)
       return user;
   }

Calling put requests in service consumers

/**
     * The consumer calls the updateUser method of the service provider
     * @return
     */
    @RequestMapping("/web/updateUser")
    public String updateUser(){
        // Logical judgment omits no writing
        // Call the method of the post request. The parameters are not spliced after the url address, and the parameters are passed through the request body
        // The form information and parameter data to be transferred (deceptive) cannot be hashmap
        MultiValueMap<String,Object> dataMap = new LinkedMultiValueMap<String,Object>();
        dataMap.add("id","1028");
        dataMap.add("name","chapter");
        dataMap.add("phone","1213141414234");

        // put has no return value
        restTemplate.put("http://01-SPRINGCLOUD-SERVICE-PROVIDER/service/updateUser", dataMap);

        return "success";
    }

DELETE request for RestTemplate

restTemplete.delete();

Delete is similar to get. Parameter passing can be spliced after the url. The delete method does not return a value.

Posted by boiy on Wed, 06 Oct 2021 14:38:03 -0700