Master “Version H & Alibaba & Link Tracking & Logging & Transaction & Lock”

The Ribbon uses a + principle + Nacos weight + combat optimization

Basic use of Ribbon

Introduction to the

Ribbon is a client load balancing tool that encapsulates Netflix Ribbon components to provide client load balancing capabilities.

Different from Nginx, the Ribbon is tied to applications. The Ribbon is not an independent service. It does not store the service list. The list is then used for load balancing and invocation.

  • Nginx independent processes perform load balancing and forward requests to different services through load balancing policies
  • Client load balancing: Saves the service list information on the client and invokes load balancing policies to allocate different services

The basic use

The Ribbon uses load balancing in two ways

  1. And RestTemplate with Ribbon+RestTemplate
  2. Combined with OpenFeign

The core sub-modules of the Ribbon

  1. Ribbon -loadbalancer: load balancing API that can be used independently or with other modules
  2. Ribbon -core: The core API of the ribbon

Order service integrated with Ribbon

The order service invokes the goods service

The configuration process consists of two steps

  1. Import ribbon dependencies in the order service

    <! --ribbon-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
    </dependency>
    Copy the code
  2. Configuration RestTemplate

The order service invokes the goods service

  1. The link that the order service invokes the commodity service cannot be written as IP + port number, but as the service name of the commodity service

  2. Restart the order service to test load balancing

Ribbon Load Balancing Simple Edition implementation process

  1. RestTemplate send request service name is http://nacos-product/product/getProductById/1
  2. To obtain@LoadBalancedAnnotated markedRestTemplate
  3. RestTemplateAdd an interceptor when usedRestTemplateIntercepts when an HTTP call is made
  4. Find an IP and port number localhost:8802 in the service list of the order service based on the service name in the URL and its own load balancing policy
  5. Access the target service and get the return result

The list of services is actually a map

Ribbon Load Balancing principle

Gets the RestTemplate of the @loadBalanced annotation tag.

The Ribbon stores all resttemplates marked with @loadBalanced into a List:

@LoadBalanced
@Autowired(required = false)
private List<RestTemplate> restTemplates = Collections.emptyList();
Copy the code

The exact location of the source code is in the LoadBalancerAutoConfiguration.

RestTemplate adds an interceptor

Interceptors are not a feature of the Ribbon

Adding an interceptor to RestTemplate requires two steps, first defining an interceptor and then adding the defined interceptor to RestTemplate.

Define an interceptor

Implement ClientHttpRequestInterceptor interface has the function of intercept requests, the interface source code is as follows:

public interface ClientHttpRequestInterceptor {
    /** * Implement this method, in this method to complete the logical content after the interception request. * In this method, the Ribbon selects a service from the * service cluster based on specific rules and sends a request to the service. * /
   ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException;

}
Copy the code

The ribbon implementation class is LoadBalancerInterceptor.

public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {

   private LoadBalancerClient loadBalancer;
   private LoadBalancerRequestFactory requestFactory;

    // omit constructor code...

   @Override
   public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
         final ClientHttpRequestExecution execution) throws IOException {
      final URI originalUri = request.getURI();
      String serviceName = originalUri.getHost();
      /** * Intercepts the request and calls the loadbalancer.execute () method * to complete the server selection within that method. Make a request to the selected server * and get the return result. * /
      return this.loadBalancer.execute(serviceName, requestFactory.createRequest(request, body, execution)); }}Copy the code

Add interceptors to the RestTemplate

RestTemplate inherits InterceptingHttpAccessor from InterceptingHttpAccessor, which provides methods for obtaining and adding interceptors.

public abstract class InterceptingHttpAccessor extends HttpAccessor {

    /** * All interceptors are saved as a List collection. * /
   private List<ClientHttpRequestInterceptor> interceptors = new ArrayList<ClientHttpRequestInterceptor>();

   /** * Sets the interceptor. * /
   public void setInterceptors(List<ClientHttpRequestInterceptor> interceptors) {
      this.interceptors = interceptors;
   }

   /** * gets the current interceptor. * /
   public List<ClientHttpRequestInterceptor> getInterceptors(a) {
      return interceptors;
   }

   // omit some code...
}
Copy the code

Using these two methods we can add the LoadBalancerInterceptor we just defined to the RestTemplate with the @loadBalanced annotation. Specific source (LoadBalancerAutoConfiguration) omit part of the code as follows:

public class LoadBalancerAutoConfiguration {

    /** * get all the strings@LoadBalancedAnnotated restTemplate */
   @LoadBalanced
   @Autowired(required = false)
   private List<RestTemplate> restTemplates = Collections.emptyList();

    / * * * create SmartInitializingSingleton interface implementation class. Spring will in all * singleton Bean initialization callback after the completion of the implementation class afterSingletonsInstantiated () method. In this method will be for all@LoadBalancedAdd the * RestTemplate annotation to the ribbon's custom LoadBalancerInterceptor. * /
   @Bean
   public SmartInitializingSingleton loadBalancedRestTemplateInitializer(
         final List<RestTemplateCustomizer> customizers) {
      return new SmartInitializingSingleton() {
         @Override
         public void afterSingletonsInstantiated(a) {
            for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
               for(RestTemplateCustomizer customizer : customizers) { customizer.customize(restTemplate); }}}}; }/** * Create Ribbon custom interceptor LoadBalancerInterceptor * Create it if the classpath is not Spring-retry. * So LoadBalancerInterceptor is the default Ribbon interceptor for intercepting requests. * /
    @Configuration
    @ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
   static class LoadBalancerInterceptorConfig {
      @Bean
      public LoadBalancerInterceptor ribbonInterceptor( LoadBalancerClient loadBalancerClient, LoadBalancerRequestFactory requestFactory) {
         return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
      }

      /** * Add interceptor concrete method. First get the current collection of interceptors (List) * then add loadBalancerInterceptor to the current collection * finally put the new collection back into the restTemplate. * /
      @Bean
      @ConditionalOnMissingBean
      public RestTemplateCustomizer restTemplateCustomizer(
            final LoadBalancerInterceptor loadBalancerInterceptor) {
         return new RestTemplateCustomizer() {
            @Override
            public void customize(RestTemplate restTemplate) {
               List<ClientHttpRequestInterceptor> list = newArrayList<>( restTemplate.getInterceptors()); list.add(loadBalancerInterceptor); restTemplate.setInterceptors(list); }}; }}}Copy the code

Now that you know the basics of how the Ribbon intercepts requests, let’s look at how the Ribbon selects servers.

Ribbon Selects server principles overview

The ribbon uses the LoadBalancerInterceptor when making a request. In the interceptor invokes LoadBalancerClient. The execute () method, this method is specific code is as follows:

@Override
public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException {
  /** * The process of creating a loadBalancer can be thought of as assembling a rule for selecting a service (IRule), a ServerList of a cluster of services (IPing), and so on * (RibbonClientConfiguration loading the configuration class), the process of the need to pay attention to the * is the process is not conducted at startup, but when a request comes to deal with. * /
   ILoadBalancer loadBalancer = getLoadBalancer(serviceId);

   /** * Select a specific Server based on ILoadBalancer. * The selection process is based on IRule, IPing, ServerList * as a reference. * /
   Server server = getServer(loadBalancer);
   if (server == null) {
      throw new IllegalStateException("No instances available for " + serviceId);
   }
   RibbonServer ribbonServer = new RibbonServer(serviceId, server, isSecure(server,
         serviceId), serverIntrospector(serviceId).getMetadata(server));

   return execute(serviceId, ribbonServer, request);
}
Copy the code

Create an ILoadBalancer class that is the core class in the Ribbon. It can be understood that it contains features such as rules for selecting services (IRule), ServerList of service clusters (IPing), and the ability to select a specific service from the service cluster according to these features. Server server = getServer(loadBalancer); This line of code just picks a specific server. Finally, the internal execute method is called, which has the following code (only the core code is preserved) :

@Override
public <T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException {
   try {
      // Initiate the call
      T returnVal = request.apply(serviceInstance);
      statsRecorder.recordStats(returnVal);
      return returnVal;
   }
   catch (IOException ex) {
      statsRecorder.recordStats(ex);
      throw ex;
   }
   catch (Exception ex) {
      statsRecorder.recordStats(ex);
      ReflectionUtils.rethrowRuntimeException(ex);
   }
   return null;
}
Copy the code

Look at the next request. Apply (serviceInstance) method specific to do those things (LoadBalancerRequestFactory) :

@Override
public ClientHttpResponse apply(final ServiceInstance instance)
      throws Exception {
   HttpRequest serviceRequest = new ServiceRequestWrapper(request, instance, loadBalancer);
   // omit some code...
   /** * make a real request. * /
   return execution.execute(serviceRequest, body);
}
Copy the code

Seeing the principle of the whole process here, let’s review the whole process with a picture:

First get all the Resttemplates that mark the @loadBalanced annotation. Then add the Ribbon’s default LoadBalancerInterceptor to the RestTemplate interceptor to intercept HTTP requests made using the RestTemplate. The ribbon default interceptor first creates an ILoadBalancer when a request is initiated (IRule, ServerList, IPing, etc.). At the code level means loading RibbonClientConfiguration configuration class). Then use ILoadBalancer to select a service from the service cluster and finally send a request to the service.

Ribbon Load balancing rules

Reference data: www.jianshu.com/p/79b9cf0d0…

The Ribbon default load balancing rules

Based on the principles of the Ribbon, IRule interface is responsible for load balancing as follows:

Rule name The characteristics of
AvailabilityFilteringRule Filter out back-end servers tagged with circuit tripped that repeatedly failed to connect and filter out those with high concurrency or use an AvailabilityPredicate that includes the logic to filter servers, Check the running status of each server recorded in status
BestAvailableRule Pick a server with minimal concurrent requests, inspect servers one by one, and skip them if they tripped
RandomRule randomSelect a Server
ResponseTimeWeightedRule Deprecated, same function as WeightedResponseTimeRule
WeightedResponseTimeRule The weightBased on response time weighting, the longer the response time, the smaller the weight and the lower the probability of being selected
RetryRule The retry mechanism is added to the load balancing policy. If the Server selection fails during the configuration period, the system tries to use subRule to select an available Server
RoundRobinRule Polling choice, poll index, and select the Server corresponding to index
ZoneAvoidanceRule The default load balancing strategy, which is to select the Server based on the performance and availability of the Server in the context of no region, is similar to polling (RandomRule).

Among them, RandomRule represents random strategy, RoundRobinRule represents polling strategy, WeightedResponseTimeRule represents weighting strategy, BestAvailableRule represents least number of requests and so on

Random source code:

Polling source:

Modify the default custom rule

The default is that polling can be changed to any rule

Let’s change it to a random algorithm

  1. Create a RestTemplate instance with load balancing function

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
    Copy the code

    When RestTemplate is used for REST operations, it automatically uses a load balancing policy. It internally adds a LoadBalancerInterceptor to the RestTemplate. This interceptor is used for load balancing.

    The polling strategy is used by default, but if you want to use other policies, specify an IRule implementation, such as:

    @Bean
    public IRule ribbonRule() {
        return new BestAvailableRule();
    }
    Copy the code

    This approach also works for OpenFeign.

Change the load balancing function to the weight configured by Nacos

  1. The cluster is weighted in NACOS

  2. In your project, choose to use NacosRule

Ribbon Optimization

Hunger is loaded

The Ribbon defaults to lazy loading, meaning that the client is created only when the call is made

ribbon:
  eager-load:
    # Open ribbon hunger loading
    enabled: true
    # configure user-Center to use the ribbon for hungry loading, separate multiple users with commas
    clients: user-center
Copy the code

Parameter tuning

Adjust the timeout period of the request and whether to retry

If the business do not do idempotence suggested turn off the retry: ribbon. MaxAutoRetriesNextServer = 0

The default time to refresh the server list from the registry is 30 seconds in ms
ribbon.ServerListRefreshInterval=15000
The connection timeout is 1 second, in ms
ribbon.ConnectTimeout=30000
The default timeout for request processing is 1 second, in ms
ribbon.ReadTimeout=30000
If this is not configured, MaxAutoRetries does not work defaults to false
#ribbon.OkToRetryOnAllOperations=true
The number of retries for the current instance defaults to 0
# ribbon.MaxAutoRetries=1
The default number of retries for switching instances is 1
ribbon.MaxAutoRetriesNextServer=0
Copy the code

If MaxAutoRetries=1 and MaxAutoRetriesNextServer=1 the request is answered within 1s. After 1 second, try again on the same server. If the request still times out or fails, try again on another service.

The ribbon timeout is as follows: ribbonTimeout = (ribbonReadTimeout + ribbonConnectTimeout) * (maxAutoRetries + 1) * (maxAutoRetriesNextServer + 1)

If you find this article helpful:

  1. Click “like” to support it, so that more people can see this content.

  2. Share your thoughts with me in the comments section, and record your thought process in the comments section.

  3. If you feel good, you can also follow the personal public account of programming deer to see more articles and explanation videos (thank you for your encouragement and support 🌹🌹🌹)