Spring Club 03 ribbon load balancing and retrying

Time:2021-1-16

1、 Ribbon serves consumers

Ribbon provides the functions of load balancing and retrying. Its bottom layer is to use resttemplate to call rest API

1.RestTemplate

Resttemplate is a rest remote calling tool provided by springboot
Its common methods are as follows:

  • Getforebject () – execute get request
  • Postforebject () – execute post request

Before the system structure is browser direct access to background services
Spring Club 03 ribbon load balancing and retrying
Later, we will demonstrate the remote call of spring cloud through a demo project
Spring Club 03 ribbon load balancing and retrying
Next, instead of using ribbon, we use resttemplate to perform remote calls

1.1 new sp06 Ribbon Project

Spring Club 03 ribbon load balancing and retrying

1.2 modify POM file

  • The ribbon dependency is already included in Eureka client
  • Need to add SP01 commons 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 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.2.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>cn.tedu</groupId>
    <artifactId>sp06-ribbon</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>sp06-ribbon</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Hoxton.RELEASE</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-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>cn.tedu</groupId>
            <artifactId>sp01-commons</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </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>

1.3 modification application.yml

spring:
  application:
    name: ribbon
    
server:
  port: 3001
  
eureka:
  client:
    service-url:
      defaultZone: http://eureka1:2001/eureka, http://eureka2:2002/eureka

1.4 main program

  • establishRestTemplateexample
    RestTemplateIt is a tool class used to call other micro services, encapsulates remote call code, and provides a set of template methods for remote callgetForObject()postForObject()etc.
package cn.tedu.sp06;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@EnableDiscoveryClient
@SpringBootApplication
public class Sp06RibbonApplication {
    //Create a resttemplate instance and store it in the spring container
    @Bean
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }
    public static void main(String[] args) {
        SpringApplication.run(Sp06RibbonApplication.class, args);
    }
}

1.5 creating a ribbon controller class

package com.tedu.sp06.controller;
import cn.tedu.sp01.pojo.Item;
import cn.tedu.sp01.pojo.Order;
import cn.tedu.sp01.pojo.User;
import cn.tedu.web.util.JsonResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import java.util.List;
@RestController
@Slf4j
public class RibbonController {
    @Autowired
 private RestTemplate rt;
    @GetMapping("/item-service/{orderId}")
    public JsonResult<List<Item>> getItems(@PathVariable String orderId) {
        //Call the remote commodity service to get the commodity list of the order
 //A station symbol format defined by {1} - resttemplate itself
 return rt.getForObject("http://localhost:8001/{1}",
                JsonResult.class,
                OrderID); // fill in the place holder {1} with OrderID
    @PostMapping("/item-service/decreaseNumber")
    public JsonResult<?> decreaseNumber(@RequestBody List<Item> items) {
        return rt.postForObject(
                "http://localhost:8001/decreaseNumber",
                items,
                JsonResult.class);
    }
    @GetMapping("/user-service/{userId}")
    public JsonResult<User> getUser(@PathVariable Integer userId) {
        return rt.getForObject("http://localhost:8101/{1}",
                JsonResult.class,
                userId);
    }
    @GetMapping("/user-service/{userId}/score") //    ?score=1000
 public JsonResult<?> addScore(@PathVariable Integer userId,
                                  @RequestParam Integer score) {
        return rt.getForObject("http://localhost:8101/{1}/score?score={2}",
                JsonResult.class,
                userId, score);
    }
    @GetMapping("/order-service/{orderId}")
    public JsonResult<Order> getOrder(@PathVariable String orderId) {
        return rt.getForObject("http://localhost:8201/{1}",
                JsonResult.class,
                orderId);
    }
    @GetMapping("/order-service/")
    public JsonResult<?> addOrder() {
        return rt.getForObject("http://localhost:8201/",JsonResult.class);
    }
}

1.6 start the service and access the test

Spring Club 03 ribbon load balancing and retrying
http://eureka1:2001
http://localhost:3001/item-service/35
http://localhost:3001/item-service/decreaseNumber
Using postman, post sends data in the following formats:
[{"id":1, "name":"abc", "number":23},{"id":2, "name":"def", "number":11}]
http://localhost:3001/user-service/7
http://localhost:3001/user-service/7/score?score=100
http://localhost:3001/order-service/123abc
http://localhost:3001/order-service/

2. Ribbon load balancing and retrying

Spring Club 03 ribbon load balancing and retrying

2.1 ribbon load balancing

Spring Club 03 ribbon load balancing and retrying

2.1.1 add ribbon dependency

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>

2.1.2 resttemplate setting@LoadBalanced

@LoadBalancedThe load balancing annotation willRestTemplateThe instance is encapsulated, the dynamic proxy object is created, and the load balancing code is cut in (AOP) to distribute the request to the servers in the cluster

package cn.tedu.sp06;
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 Sp06RibbonApplication {
    @Loadbalanced // Notes on load balancing
    @Bean
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }
    public static void main(String[] args) {
        SpringApplication.run(Sp06RibbonApplication.class, args);
    }
}

2.1.3 setting the access path to service ID

package cn.tedu.sp06.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import cn.tedu.sp01.pojo.Item;
import cn.tedu.sp01.pojo.Order;
import cn.tedu.sp01.pojo.User;
import cn.tedu.web.util.JsonResult;
@RestController
public class RibbonController {
    @Autowired
    private RestTemplate rt;
    @GetMapping("/item-service/{orderId}")
@HystrixCommand(fallbackMethod = "getItemsFB")
public JsonResult<List<Item>> getItems(@PathVariable String orderId) {
    //Call the remote commodity service to get the commodity list of the order
    //{1} - a stop character format defined by resttemplate itself
@PostMapping("/item-service/decreaseNumber")
public JsonResult<?> decreaseNumber(@RequestBody List<Item> items) {
    return rt.postForObject("http://item-service/decreaseNumber", items, JsonResult.class);
}
@GetMapping("/user-service/{userId}")
public JsonResult<User> getUser(@PathVariable Integer userId) {
    return rt.getForObject("http://user-service/{1}", JsonResult.class, userId);
}
@GetMapping("/user-service/{userId}/score") // ?score=1000
public JsonResult<?> addScore(@PathVariable Integer userId, @RequestParam Integer score) {
    return rt.getForObject("http://user-service/{1}/score?score={2}", JsonResult.class, userId, score);
}
@GetMapping("/order-service/{orderId}")
public JsonResult<Order> getOrder(@PathVariable String orderId) {
    return rt.getForObject("http://order-service/{1}", JsonResult.class, orderId);
}
@GetMapping("/order-service")
public JsonResult<?> addOrder() {
    return rt.getForObject("http://order-service", JsonResult.class);
}

2.1.4 access test

Spring Club 03 ribbon load balancing and retrying
Spring Club 03 ribbon load balancing and retrying

2.2 ribbon retrying

Spring Club 03 ribbon load balancing and retrying

2.2.1 add ribbon dependency

<dependency>
    <groupId>org.springframework.retry</groupId>
    <artifactId>spring-retry</artifactId>
</dependency>

2.2.2 configuration application.yml

spring:
  application:
    name: ribbon   
server:
  port: 3001
#Connecting Eureka
eureka:
  client:
    service-url:
      defaultZone: http://eureka1:2001/eureka, http://eureka2:2002/eureka
ribbon:
  #Number of retries per unit
 MaxAutoRetries: 1
  #Number of server changes
 MaxAutoRetriesNextServer: 2

2.2.3 the main program sets the timeout property of resttemplate’s request factory

package com.tedu.sp06;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
@Enable circuit breaker // enable circuit breaker
public class Sp06RibbonApplication {
   public static void main(String[] args) {
      SpringApplication.run(Sp06RibbonApplication.class, args);
   }
   //Create a resttemplate instance and store it in the spring container
 @Loadbalanced // load balancing annotation, encapsulating resttemplate and enhancing the function of resttemplate
 @Bean
 public RestTemplate getRestTemplate (){
      SimpleClientHttpRequestFactory f=new SimpleClientHttpRequestFactory();
      //The two timeout parameters are - 1 by default, indicating that they are not enabled
 f.setConnectTimeout(1000);
      f.setReadTimeout(1000);
      return new RestTemplate();
}
}

2.2.4 add delay code to itemcontroller to test the retrial mechanism of ribbon

package cn.tedu.sp02.item.controller;
import cn.tedu.sp01.pojo.Item;
import cn.tedu.sp01.service.ItemService;
import cn.tedu.web.util.JsonResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Random;
@RestController
@Slf4j
public class ItemController {
    @Autowired
 private ItemService itemService;
    //To test multiple servers in the cluster later, inject the port number here
 @Value("${server.port}")
    private int port;
    @GetMapping("/{orderId}")
    public JsonResult<List<Item>> getItems(@PathVariable String orderId) throws InterruptedException {
        log.info("orderId="+orderId+", port="+port);
        //Add random delay code
        if (Math.random()<0.9){
        //Randomly generate random delay time from 0 to 5000
            long t=new Random().nextInt(5000);
            log.info (delay: + T);
            Thread.sleep(t);
        }
       //
        List<Item> items = itemService.getItems(orderId);
        return JsonResult.ok().msg("port="+port).data(items);
    }
    //Only post requests are processed
 //The @ requestbody annotation can receive the post request protocol body data completely
 @PostMapping("/decreaseNumber")
    public JsonResult<?> decreaseNumber(@RequestBody List<Item> items) {
        itemService.decreaseNumber(items);
        return  JsonResult.ok (). MSG ("reduce commodity inventory successfully");
    }
}

2.2.5 test the ribbon retrial mechanism

Spring Club 03 ribbon load balancing and retrying
Spring Club 03 ribbon load balancing and retrying