Microservice architecture | 4.1 detailed explanation of Ribbon based load balancing

preface

reference material:
<Spring Microservices in Action>
Principle and practice of Spring Cloud Alibaba microservice
"Spring cloud framework development tutorial in Silicon Valley of station B" Zhou Yang

Spring Cloud Ribbon is a set of client-side load balancing tools based on Netflix Ribbon; Provide the software load balancing algorithm and service call of the client;

1. Ribbon Basics

1.1 what is ribbon

  • Spring Cloud Ribbon is a set of client-side load balancing tools based on Netflix Ribbon; Provide the software load balancing algorithm and service call of the client;
  • Ribbon client component provides a series of perfect configuration items, such as connection timeout, Retry, etc. To put it simply, list all the machines behind the Load Balancer (LB) in the configuration file, and the ribbon will automatically connect these machines based on certain rules (such as simple polling, random connection, etc.);
  • You can easily use Ribbon to implement custom load balancing algorithm;

1.2 three levels of interaction with Ribbon

  • Spring discovery client: provides the lowest level access to the registration services cached in the Ribbon and Ribbon;
  • Spring DiscoveryClient with RestTemplate enabled;
  • Netflix Feign client;

1.3 Ribbon works in two steps

  • The first step is to select Eureka server, which gives priority to servers with less load in the same area;
    The second step is to select an address from the service registration list obtained from the server according to the policy specified by the user;

1.4 service providers and consumers

  • Providers: service providers register themselves in the Registration Center for consumers to find; In this example, there are multiple providers providing services to consumers;
  • Consumers: consumers use service discovery, find providers and invoke provider services; In this example, only one consumer selects one of multiple providers to serve himself;

1.5 Ribbon core component IRule

  • Select a service to be accessed from the service list according to the specific algorithm;
  • The way of load balancing is defined;
  • There are several ways to realize load balancing:
    • Roundrobin rule: polling;
    • RandomRule: random;
    • RetryRule: first obtain the service according to the roundrobin rule policy. If the service acquisition fails, it will retry within the specified time to obtain the available service;
    • WeightedResponseTimeRule: the extension of roundrobin rule. The faster the response speed, the greater the instance selection weight, and the easier it is to be selected;
    • BestAvailableRule: first filter out the services in the circuit breaker tripping state due to multiple access faults, and then select a service with the least concurrency;
    • Availability filtering rule: filter out the failed instances first, and then select the instances with smaller concurrency;
    • ZoneAvoidanceRule: the default rule, which determines the performance of the region where the server is located and the availability of the server, and selects the server;

2. Three level examples of service consumers acquiring providers

2.1 introduction of POM XML dependency

<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
  • If Eureka is used as the registration center, the dependency need not be introduced, because Eureka's dependency includes ribbon related dependency jar packages;

2.2 using Spring DiscoveryClient to find service instances

2.2.1 add notes on the main program class

  • @EnableDiscoveryClient: indicates that it can be discovered by the registry. It is the trigger of Sring Cloud. Its function is to enable applications to use DiscoveryClient and Ribbon libraries;

2.2.2 using DiscoveryClient to find information

Under the client package of the service consumer;

@Component
public class ProviderDiscoveryClient {

    //Automatically inject the DiscoveryClient class, which is used to interact with the Ribbon
    @Autowired
    private DiscoveryClient discoveryClient;

    public Provide getProvide(String providerId) {
        RestTemplate restTemplate = new RestTemplate();
        //Get the list of all instances of the service provider. ServiceInstance is used to save specific instances about the service (including host name, port and URL)
        List<ServiceInstance> instances = discoveryClient.getInstances("provider-instance-name");

        if (instances.size()==0) return null;
        //Retrieve the service endpoint to invoke
        String serviceUri = String.format("%s/providers/%s",instances.get(0).getUri().toString(), providerId);

		//Use the standard Spring REST template class to call the service
        ResponseEntity< provider > restExchange =
                restTemplate.exchange(
                        serviceUri,
                        HttpMethod.GET,
                        null, Provider.class, providerId);
        
        return restExchange.getBody();
    }
}
  • This method has the following problems:

    • The client load balancing of Ribbon is not used, and which service instance to call needs to be defined by the developer;
    • The developer must build a URL to invoke the service;
    • Instantiate ResTemplate class, which does not comply with Spring IoC specification;
  • Combined with 5. Implementation of local load balancer (consumer), it can be used for client load balancing, that is, the developer defines the local load balancer to realize load balancing;

2.3 using Spring RestTemplate with Ribbon function to call service

2.3.1 add comments on the main program class

@SpringBootApplication //Just need this annotation
public class Application {
  @LoadBalanced //Tell Spring Cloud to create a RestTemplate that supports Ribbon
  @Bean
  public RestTemplate getRestTemplate(){
      return new RestTemplate();
  }
  public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
  }
}
  • In earlier versions of Spring Cloud, the RestTemplate class automatically supports Ribbon by default;
  • Since Spring Cloud released Angel version, RestTemplate in Spring Cloud no longer supports Ribbon;
  • Therefore, subsequent versions must explicitly annotate with @ LoadBalanced annotation before Ribbon and RestTemplate can be used together;
  • *RestTemplate is not necessarily placed in the main program class; You can also create a new ApplicationContextConfig configuration class under the config package and put the RestTemplate in this class:
@Configuration
public class ApplicationContextConfig{
    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
}

2.3.2 use the RestTemplate of Ribbon to call the service

Under the client package of the service consumer;

@Component
public class ProviderRestTemplateClient {
    //It can be injected automatically without instantiation
    @Autowired
    RestTemplate restTemplate;

    public Provider getProvider(String providerId){
        ResponseEntity<Provider> restExchange =
                restTemplate.exchange(
                        //Use Eureka service ID to build the target URL
                        "http://provider-instance-name/providers/{providerId}",
                        HttpMethod.GET,
                        null, Provider.class, providerId);
        return restExchange.getBody();
    }
}
  • By using the RestTemplate class, the Ribbon will poll all load balancing requests among all service instances;

2.4 calling services using Netflix Feign client

Feign related knowledge will be explained in the next chapter 4.2 service interface invocation based on feign and OpenFeign. Here, we only focus on the difference and comparison with the above two invocation provider services;

2.4.1 add notes on the main program class

@Enablefeign clients: indicates to enable Feign clients;

2.4.2 define Feign interface for calling service provider

@FeignClient("provider-instance-name") //Identify the client whose service is feign
public interface ProviderFeignClient {
    //Define the path and action of the endpoint
    @RequestMapping( 
            method= RequestMethod.GET,
            value="/providers/{providerId}",
            consumes="application/json")
    //Define the parameters of the incoming endpoint, which can be called by the client to trigger the organization service        
    Provider getProvider(@PathVariable("providerId") String providerId);
}
  • If the ProviderFeignClient class is used, developers only need to automatically assemble and use it;

3. Example of custom load balancing algorithm through java configuration class (consumer service)

It refers to switching the default load balancing algorithm. After switching, it is still ready-made (different from the local load balancer, which should be implemented by itself);

3.1 write configuration class

  • Note: the custom configuration class cannot be placed under the current package and sub package scanned by @ ComponentScan, otherwise the custom configuration class will be shared by all Ribbon clients and cannot achieve the purpose of customization;
  • @The ComponentScan annotation is encapsulated into the @ SpringBootApplication annotation on the main boot class. By default, it scans the package of the main startup class and its sub packages, so we need to return to the upper directory and create a new myRule directory to store our customized load balancing configuration class;

@Configuration
public class MySelfRule {
    @Bean
    public IRule myRule(){
        return new RandomRule();//Defined as random
    }
}

3.2 add notes on the main startup class

  • @RibbonClient(name = "provider instance name", configuration=MySelfRule.class): indicates the user-defined load balancing algorithm is used;
    • Name: Specifies the instance name of the service provider;
    • Configuration: specify which configuration class to use for load balancing;
    • Indicates that the provider service uses the Ribbon configuration corresponding to MySelfRule;
  • Similarly, the RestTemplate class needs to display the declaration with the @ LoadBalanced annotation;

4. Configure a custom load balancing algorithm example (consumer service)

It refers to switching the default load balancing algorithm. After switching, it is still ready-made (different from the local load balancer, which should be implemented by itself);

4.1 modify bootstrap YML profile

  • The effect of the above java configuration class is equivalent to the following configuration file:
#Instance name of the service provider
provider-instance-name:
  ribbon:
    #Represents the load balancing strategy used by the Ribbon. The value of the attribute is: the implementation class of IRule
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
    
    #Other available configuration properties
    # NFLoadBalancerClassName: configure the implementation class of ILoadBalancer
    # NFLoadBalancerPingClassName: configure the implementation class of IPing
    # NIWSServerListClassName: configure the implementation class of ServerList
    # NIWSServerListFilterClassName: configure the implementation class of ServerListtFilter

4.2 main program

  • No @ RibbonClient annotation is required;
  • Similarly, the RestTemplate class needs to display the declaration with the @ LoadBalanced annotation;

5. Implementation of local load balancer (consumer)

The local load balancer is different from the custom load balancing algorithm; The load balancing algorithm of the former needs to be implemented manually, while the latter just switches to another ready-made load balancing algorithm;

5.1 do not use RestTemplate

  • That is, the main program class does not need to display the declaration with @ LoadBalanced annotation on the RestTemplate class;
  • The @ LoadBalanced annotation can be deleted or annotated;

5.2 define load balancing interface

You can create a new package to store our own load balancing algorithm;

public interface LoadBalancer{
    ServiceInstance instances(List<ServiceInstance> serviceInstances);
}

5.3 realize load balancing interface

  • You can write different load balancing algorithms according to business requirements. Here is only one example;
  • This example implements a relatively simple atomic load balancing algorithm;
@Component
public class MyLB implements LoadBalancer{
    private AtomicInteger atomicInteger = new AtomicInteger(0);
    public final int getAndIncrement(){
        int current;
        int next;

        do {
            current = this.atomicInteger.get();
            next = current >= 2147483647 ? 0 : current + 1;
        }while(!this.atomicInteger.compareAndSet(current,next));
        System.out.println("*****Number of visits next: "+next);
        return next;
    }

    //Load balancing algorithm: the number of requests of the rest interface% the total number of server clusters = the subscript of the actual calling server location. The count of the rest interface starts from 1 after each service restart.
    @Override
    public ServiceInstance instances(List<ServiceInstance> serviceInstances){
        int index = getAndIncrement() % serviceInstances.size();
        return serviceInstances.get(index);
    }
}

5.4 using local load balancer in controller interface

  • Similar to "2.2 using Spring DiscoveryClient to find service instances";
  • The difference is that there is no load balancing function in 2.2. Here, on the basis of 2.2, the developer defines the local load balancer and does not use the load balancing provided by the Ribbon. Therefore, it is not necessary to use the @ LoadBalanced annotation to display the declaration for the RestTemplate class mentioned in "5.1 do not use RestTemplate";
@RestController
public class OrderController{
    //The name of the service provider example
    public static final String PAYMENT_URL = "http://provider-instance-name";
    @Resource
    private RestTemplate restTemplate;
    @Resource
    private LoadBalancer loadBalancer;
    @Resource
    private DiscoveryClient discoveryClient;
 
    @GetMapping(value = "/provider/mylb")
    public String getProviderLB(){
        //Get a list of all instances of the service provider
        List<ServiceInstance> instances = discoveryClient.getInstances("provider-instance-name");
        if(instances == null || instances.size() <= 0){
            return null;
        }
        //Select the service provider using the local load balancer
        ServiceInstance serviceInstance = loadBalancer.instances(instances);
        URI uri = serviceInstance.getUri();
        return restTemplate.getForObject(uri+"/provider/mylb",String.class);
    }
}

last

Newcomer production, if there are mistakes, welcome to point out, thank you very much! Welcome to the official account and share some more everyday things. If you need to reprint, please mark the source!

Keywords: Load Balance Distribution Spring Cloud Microservices ribbon

Added by watsonowen on Thu, 27 Jan 2022 18:56:19 +0200