Spring Cloud Study Notes: Ribbon Load Balancing

Ribbon

Spring Cloud Ribbon is a set of client load balancing tools based on Netflix Ribbon.
Ribbon is an open source project released by Netflix. Its main function is to provide client-side software load balancing algorithms and service calls. The Ribbon client component provides a series of complete configuration items such as connection timeout, retry, etc. Simply put, it is to list all the machines behind Load Balancer in the configuration file, and Ribbon will automatically help to connect these machines based on certain rules (such as simple polling, random connection, etc.). We can easily implement custom load balancing algorithms using Ribbon.

Ribbon is divided into two steps when working. The first step is to select EurekaServer, which preferentially selects server s with less load in the same region. The second step is to select an address from the service registration list obtained by the server according to the policy specified by the user. Among them, Ribbon provides a variety of strategies: such as round-robin, random and weighted according to response time.
In a word: Ribbon is load balancing + RestTemplate call, and finally realizes the remote call of RPC.

RestTemplate use

The RestTemplate class provided by the Spring framework can be used to call REST services in applications. It simplifies the communication with http services, unifies RESTful standards, and encapsulates http links. You only need to pass in the url and return value type. Compared with the commonly used HttpClient, RestTemplate is a more elegant way to call RESTful services.

getForObject method

getForEntity method

cloud-consumer-order80.OrderController

@GetMapping("/consumer/payment/getforentiy/{id}")
    public CommonResult<Payment> getPayment2(@PathVariable("id") Long id){
        ResponseEntity<CommonResult> entity = restTemplate.getForEntity(PAYMENT_URL + "/payment/get/" + id, CommonResult.class);
        if(entity.getStatusCode().is2xxSuccessful()){
            log.info(entity.getStatusCode()+"\t"+entity.getHeaders());
            return entity.getBody();
        }else {
            return new CommonResult<>(444,"operation failed");
        }
    }

RestTemplate call succeeded

Core component IRule

Pick a service to access from a list of services based on a specific algorithm

  • com.netflix.loadbalancer.RoundRobinRule: Round Robin
  • com.netflix.loadbalancer.RandomRule: random
  • com.netflix.loadbalancer.RetryRule: First obtain the service according to the RoundRobinRule policy. If the service fails to be obtained, it will retry within the specified time to obtain the available service.
  • WeightedResponseTimeRule: an extension to RoundRobinRule, the faster the response speed is, the greater the selection weight, the easier it is to be selected
  • BestAvailableRule: It will first filter out services that are in a state of circuit breaker tripping due to multiple access failures, and then select a service with the least concurrency
  • AvailabilityFilteringRule: Filter out faulty instances first, and then select instances with smaller concurrency
  • ZoneAvoidanceRule: The default rule, which combines the performance of the region where the server is located and the availability of the server to select the server

Load rule replacement


Rule: This custom configuration class cannot be placed under the current package and subpackages scanned by @ComponentScan, otherwise the custom configuration class will be shared by all Ribbon clients, which will not achieve the purpose of special customization. The current package and subpackages scanned by @ComponentScan are the package where the Spring Boot main program is located, because @ComponentScan is included in the @SpringBootApplication annotation.

MySelfRule

/**
 * Custom load balancing rule class
 */
@Configuration
public class MySelfRule {
    @Bean
    public IRule myRule(){
        return new RandomRule();
    }
}

The main startup class is annotated @RibbonClient, and our custom Ribbon configuration class can be loaded when the microservice is started, so that the configuration takes effect

@SpringBootApplication
@EnableEurekaClient
@RibbonClient(name="CLOUD-PROVIDER-SERVICE",configuration = MySelfRule.class)
public class OrderMain80 {
    public static void main(String[] args){
        SpringApplication.run(OrderMain80.class,args);
    }
}

The name of the microservice in the annotation must be the same as the name of the microservice registered in Eureka, with strict capitalization.

Default load: principle of polling algorithm


The source code is implemented in RoundRobinRule.java using CAS spin lock

...
private int incrementAndGetModulo(int modulo) {
    int current;
    int next;
    do {
        current = this.nextServerCyclicCounter.get();
        next = (current + 1) % modulo;
    } while(!this.nextServerCyclicCounter.compareAndSet(current, next));

    return next;
}
...

Handwritten polling algorithm

Add methods in the controller layer of 8001 and 8002 on the service provider side

@GetMapping(value = "/payment/lb")
public String getPaymentLB(){
    return servicePort;
}

Remove the @LoadBalanced annotation from the 80-side applicationContextConfig

@Configuration
public class ApplicationContextConfig {
    @Bean
//    @LoadBalanced
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
}

Then create the lb package under the current package to write the interface and implementation class

LoadBanlancer.java

/**
 * Get the total number of live services on Eureka
 */
public interface LoadBanlancer {
    ServiceInstance instance(List<ServiceInstance> serviceInstanceList);
}

MyLB.java

/**
 * Handwritten polling algorithm
 */
@Component
public class MyLB implements LoadBanlancer {
    //Atomic class
    private AtomicInteger atomicInteger = new AtomicInteger(0);

    public final int getAndIncrement(){
        int current;
        int next;
        //Spinlocks can also be used with for(;;)
        do{
            current = this.atomicInteger.get();
            //Exceeded maximum value, 0, recount 2147483647 Integer.MAX_VALUE
            next = current >= 2147483647 ? 0 : current + 1;
        }while (!this.atomicInteger.compareAndSet(current,next));
        System.out.println("the first"+next+"visits,next:"+next);
        return next;
    }

    @Override
    public ServiceInstance instance(List<ServiceInstance> serviceInstance) {
        int index = getAndIncrement() % serviceInstance.size();
        return serviceInstance.get(index);
    }
}

Modify the controller layer method

@RestController
@Slf4j
public class OrderController {
//    private static final String PAYMENT_URL = "http://localhost:8001";
    private static final String PAYMENT_URL = "http://CLOUD-PROVIDER-SERVICE";

    @Resource
    private RestTemplate restTemplate;

    /**
     * Custom load balancing rules
     */
    @Resource
    private LoadBanlancer loadBanlancer;
    @Resource
    private DiscoveryClient discoveryClient;

    /**
     * Routing Rules: Polling
     * @return
     */
    @GetMapping(value = "/consumer/payment/lb")
    public String getPaymentLB(){
        List<ServiceInstance> instances = discoveryClient.getInstances("cloud-provider-service");
        if(instances == null || instances.size() <= 0){
            return null;
        }
        ServiceInstance serviceInstance = loadBanlancer.instance(instances);
        URI uri = serviceInstance.getUri();
        return restTemplate.getForObject(uri+"/payment/lb",String.class);
    }
}

Start the microservice and access the corresponding url

Difference between Ribbon and Nginx

The difference between Ribbon local load balancing client and Nginx server load balancing:
Ribbon local load balancing is that when calling the microservice interface, it will obtain the registration information service list from the registry and cache it locally in the JVM, so as to realize the RPC remote service invocation technology locally.
Nginx is a server load balancing, all client requests will be handed over to nginx, and then nginx will forward the requests. That is, load balancing is implemented by the server.

Tags: Java Spring Cloud

Posted by KeitaroHimura on Tue, 31 May 2022 14:25:49 +0530