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
- And RestTemplate with Ribbon+RestTemplate
- Combined with OpenFeign
The core sub-modules of the Ribbon
- Ribbon -loadbalancer: load balancing API that can be used independently or with other modules
- 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
-
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
-
Configuration RestTemplate
The order service invokes the goods service
-
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
-
Restart the order service to test load balancing
Ribbon Load Balancing Simple Edition implementation process
- RestTemplate send request service name is http://nacos-product/product/getProductById/1
- To obtain
@LoadBalanced
Annotated markedRestTemplate
RestTemplate
Add an interceptor when usedRestTemplate
Intercepts when an HTTP call is made- 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
- 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
-
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
-
The cluster is weighted in NACOS
-
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:
-
Click “like” to support it, so that more people can see this content.
-
Share your thoughts with me in the comments section, and record your thought process in the comments section.
-
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 🌹🌹🌹)