1. The background

Today we’ll learn about SpringCloud’s client-side load balancing Ribbon

We continue to use the previous Eureka-Server as the service registry today

The following versions use Springboot and SpringCloud

  • Springboot version: 2.3.5-release
  • Springcloud version: Hoxton SR9

2. What is the Ribbon

The Ribbon is an open source client load balancer released by Netflix. It is an important part of SpringCloud-Netflix that connects Netflix’s mid-tier services together. The Ribbon client component provides a comprehensive set of configuration options, such as connection timeout and retry. Simply put, if the Ribbon lists all the services behind Load Balancer in the configuration file, the Ribbon will automatically connect these services based on certain rules (e.g. simple polling, random linking, etc.). You can easily implement custom Load balancing algorithms.

What are server side load balancing and client side load balancing

Server load balancing:

For example, Nginx/H5 carries out load balancing through Nginx. It sends requests first, and then selects one to access from multiple servers through load balancing algorithm. That is, load balancing algorithm is allocated on the server

Client load balancing:

For example, the Ribbon in Spring Cloud has a server address list on the client side. Before sending a request, a server is selected by load balancing algorithm and then accessed. This is client load balancing. That is, load balancing algorithm is allocated on the client.

3. The role of the Ribbon

The Ribbon is a key function point for load balancing access services on the client side

  • 1 Service discovery Discover the list of dependent services
  • 2 Service Selection Rules How to select a valid service among multiple services
  • 3 Service monitoring Detect invalid services and eliminate invalid services efficiently

4. Springcloud Integrated Ribbon (RestTemplate+Ribbon)

Projects are divided into producers and consumers

Consumers are divided into three steps

Add dependencies inside the ribbon dependencies

	<dependencies>
	  <dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
		</dependency>
	</dependencies>
Copy the code

Add annotations @ EnableDiscoveryClient

Add the configuration

@Configuration
public class Config {

    @LoadBalanced
    @Bean
    public RestTemplate restTemplate(a){
        return newRestTemplate(); }}Copy the code
Production is divided into 3 steps > Add dependencies which contain the ribbon dependencies
	<dependencies>
	  <dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
		</dependency>
	</dependencies>
Copy the code

Add annotations @ EnableDiscoveryClient

Add the configuration

4.2 Service Code

consumers

@Data
public class User {

    private String id;

    private String name;
    private int age;

    private  int port;

    @Override
    public String toString(a) {
        return "User{" +
                "id='" + id + '\' ' +
                ", name='" + name + '\' ' +
                ", age=" + age +
                ", port=" + port +
                '} '; }}@RequestMapping("/api/comsumer/user")
@RestController
public class UserController {

    @Autowired
    RestTemplate restTemplate;

    @GetMapping("/{id}")
    public String selectUser(@PathVariable("id") String id){
        ResponseEntity<String> responseEntity = restTemplate.getForEntity("http://ms-ribbon-producer/api/user/{id}",String.class,id);
        returnresponseEntity.getBody(); }}Copy the code

producers


@RequestMapping("/api/user")
@RestController
public class UserController {
    @Value("${server.port}")
    Integer port;

    @GetMapping("/{id}")
    public String selectUser(@PathVariable("id") String id){
        User user = new User();
        user.setId(id);
        user.setName("wangyunqi");
        user.setPort(port);
        returnuser.toString(); }}Copy the code

validation

  1. Producer: 8002,8003 port
  2. Customer: Port 8001

Using the consumer 8001 interface, it is found that the returned data is 8002,8003. Training in rotation mode

5.Ribbon load balancing algorithm

  • 1RoundRobinRule default polling rule
  • 2RandomRule Gets a service at random
  • 3AvailabilityFilteringRule

This load balancer rule filters out the services that are tripping due to multiple access failures and the number of concurrent connections exceeds the threshold and then accesses the rest of the service list according to a polling policy

  • 4 weightedresponsetimerule calculated according to the weight of all the services the average response time for the faster response time service is larger, the higher the probability of the selected weights. If the statistics are insufficient, the WeightedResponseTimeRule switches to the WeightedResponseTimeRule using RoundRobinRule.
  • 5RetryRule Obtains the service based on the RoundRobinRule policy. If the service fails to be obtained, the system tries again to obtain available services within a specified period
  • The load balancer first filters out services that are in the circuit breaker trip state due to multiple access failures and then selects the service with the least concurrency
  • 7ZoneAvoidanceRule default rule selects a Server based on the performance of the zone where the Server is located and the availability of the Server

6 How do I modify the default load balancing algorithm

@Configuration
public class Config {

    // Change the mode to random
    @Bean
    public IRule rule(a){
        return newRandomRule(); }}Copy the code

6 How can I customize the load balancing algorithm

  1. Inheritance AbstractLoadBalancerRule
  2. Override the choose method
Public class CustomRandomRule extends AbstractLoadBalancerRule {Random rand; public CustomRandomRule() { rand = new Random(); } private int currentIndex = 0; private List<Server> currentChooseList = new ArrayList<Server>(); /** * Randomly choose from all living servers */ public Server choose(ILoadBalancer lb, Object key) { if (lb == null) { return null; } Server server = null; while (server == null) { if (Thread.interrupted()) { return null; } List<Server> upList = lb.getReachableServers(); List<Server> allList = lb.getAllServers(); int serverCount = allList.size(); if (serverCount == 0) { return null; } // select a random index int index = rand. NextInt (serverCount); / / the current poll less than or equal to 5 times the if (currentIndex < 5) {/ / save the currently selected list service IP if (currentChooseList. IsEmpty ()) { currentChooseList.add(upList.get(index)); } // The current ++ currentIndex+; // Return currentChooselist.get (0); }else { currentChooseList.clear(); currentChooseList.add(0,upList.get(index)); currentIndex=0; } 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 } }Copy the code