Spring Cloud explain (c) Ribbon load balancing principle

Ribbon is a client load balancer, given the application of some of the power to dominate HTTP and TCP behavior, which can be learned, here's client load balancing load balancing is an internal process. Ribbon in SpringCloud ecological indispensable component, without the Ribbon, services can not scale. Feign, Zuul have been integrated Ribbon.

1. Ribbon load balancing strategy

The Ribbon provides seven load balancing strategy

Strategy name description
RandomRule Random Strategy Server randomly selected
RoundRobinRule Polling Policy Server in order to cycle
RetryRule Retry strategy In a configurable time period, when the selected Server is unsuccessful, it has been trying to select one of the available Server
BestAvailableRule Minimum concurrency strategy One by one investigation Server, Server is opened if the circuit breaker is ignored, select the lowest concurrent connections Server not be ignored in Server
AvailabilityFilteringRule Available filter test Connection failure has been filtered off, and was not labeled circuit tripped (i.e., not available) the Server, the Server to filter out high concurrency
ResponseTimeWeightedRule Response time weighting strategy The response time of the assigned weight Server weight, the longer the response time, the lower the weight, the lower the probability of being selected to
ZoneAvoidanceRule Regional Balance Policy Integrated Area Analyzing polling availability and performance of Server Server Server selection, and determines a running performance of the AWS Zone is available, unavailable Zone remove all Server

Ribbon default load balancing strategy is polling policy .

2. Ribbon Configuration

2.1 load balancing policy configuration

You can configure each service load balancing policy in the configuration file

{instance-id}: # instance-id 即被调用服务名称
    ribbon:
         NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
    #    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #配置规则 随机
    #    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule #配置规则 轮询
    #    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RetryRule #配置规则 重试
    #    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.WeightedResponseTimeRule #配置规则 响应时间权重
    #    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.BestAvailableRule #配置规则 最空闲连接策略

Timeout and retry configuration 2.2

{instance-id}: # instance-id 指的是被调用者的服务名称
  ribbon:
    ConnectTimeout: 30000 # 链接超时时间
    ReadTimeout: 30000 # 读超时时间
    MaxAutoRetries: 1 # 对第一次请求的服务的重试次数
    MaxAutoRetriesNextServer: 1 # 要重试的下一个服务的最大数量(不包括第一个服务)
    OkToRetryOnAllOperations: true # 是否对 连接超时、读超时、写超时 都进行重试

2.3 hunger load

Ribbon when performing load balancing is not started when the load on the line on paper, but in practice the request, only to request context information acquisition callee ip, port, when this mode is inferior in a network environment, They tend to make a cause timeouts, resulting in failure of the first call. At this point you need to specify Ribbon client, a hunger load , namely: load at startup good context.

ribbon:
  eager-load:
    enabled: true
    clients: spring-cloid-ribbon-provider

At this point start consumer, you will see the print control information is as follows:

Client: spring-cloid-ribbon-provider instantiated a LoadBalancer: DynamicServerListLoadBalancer:{NFLoadBalancer:name=spring-cloid-ribbon-provider,current list of Servers=[],Load balancer stats=Zone stats: {},Server stats: []}ServerList:null
Using serverListUpdater PollingServerListUpdater
DynamicServerListLoadBalancer for client spring-cloid-ribbon-provider initialized: DynamicServerListLoadBalancer:{NFLoadBalancer:name=spring-cloid-ribbon-provider,current list of Servers=[],Load balancer stats=Zone stats: {},Server stats: []}ServerList:org.springframework.cloud.netflix.ribbon.eureka.DomainExtractingServerList@79e7188e

You can see when you start to load up  spring-cloud-ribbon-provider, and boundLoadBalancer。

2.4 common arrangement

Configuration Item Explanation
{instance-id}:ribbon.NFLoadBalancerClassName Load balancing refers classpath
{instance-id}:ribbon:NFLoadBalancerRuleClassName Specifies the load balancing algorithm classpath
{instance-id}:ribbom:NFLoadBalancerPingClassName Specifies the survival of the class path testing services
{instance-id}:ribbon:NIWSServerListClassName Specify a class path to get the list of services
{instance-id}:ribbon:NIWSServerListFilterClassName Filter the specified service implementation class path

3. Works

3.1 Ribbon core interface

interface description The default implementation class
IClientConfig Ribbon is defined in the configuration management interface DefaultClientConfigImpl
IRule The definition of the Ribbon interface load balancing strategy RoundRobinRule
IPing Ping service definitions regularly check the availability of an interface DummyPing
ServerList<Server> Get a list of methods defined service interfaces ConfigurationBasedServerList
ServerListFilter<Server> Define a specific list of methods expectations for service interface ZonePreferenceServerListFilter
ILoadBalanacer Interface core of the method defined load balancing service selection BaseLoadBalancer
ServerListUpdater Define the interface dynamically updated list of services for DynamicServerListLoadBalancer PollingServerListUpdater

The operating principle 3.2 Ribbon

Ribbon load balancing, basic usage is to inject a RestTemplate, and use @LoadBalanced on RestTemplate, in order to make RestTemplate with load balancing.

3.2.1 @LoadBalanced

This annotation label, a RestTemplate use LoadBalancerClient.

/**
 * Annotation to mark a RestTemplate bean to be configured to use a LoadBalancerClient
 * @author Spencer Gibb
 */
@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface LoadBalanced {
}

LoadBalancerClient:

/**
 * Represents a client side load balancer
 * @author Spencer Gibb
 */
public interface LoadBalancerClient extends ServiceInstanceChooser {

    <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException;

    <T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException;
    
    URI reconstructURI(ServiceInstance instance, URI original);
}

LoadBalancerClient has expanded ServiceInstanceChooser Interface

public interface ServiceInstanceChooser {

    ServiceInstance choose(String serviceId);
}

Description:

  • ServiceInstance choose(String serviceId): According to the serviceId, in conjunction with the load balancer, to select a service instance
  • <T> T execute(String serviceId, LoadBalancerRequest<T> request): Use LoadBalancer for the top of serviceInstance service execution request
  • <T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request): Use ServiceInstance LoadBalancer for execution request from the service specified, a method is overloaded, the details of implementation of the method on a
  • URI reconstructURI(ServiceInstance instance, URI original): Using the host ip, port to build a specific URI, RIbbon for internal use. Ribbon using the service URI names as host. Such as: HTTP: // instance-the above mentioned id / path / to / Service

3.2.2 LoadBalancer initialization

LoadBalancerAutoConfiguration Is the key to the core code Ribbon load balancing load the class initialization, start as follows:

@Configuration
@ConditionalOnClass(RestTemplate.class)
@ConditionalOnBean(LoadBalancerClient.class)
@EnableConfigurationProperties(LoadBalancerRetryProperties.class)
public class LoadBalancerAutoConfiguration {
    @Bean
    @ConditionalOnMissingBean
    public LoadBalancerRequestFactory loadBalancerRequestFactory(
            LoadBalancerClient loadBalancerClient) {
        return new LoadBalancerRequestFactory(loadBalancerClient, transformers);
    }

    @Configuration
    @ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
    static class LoadBalancerInterceptorConfig {
        @Bean
        public LoadBalancerInterceptor ribbonInterceptor(
                LoadBalancerClient loadBalancerClient,
                LoadBalancerRequestFactory requestFactory) {
            return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
        }

        @Bean
        @ConditionalOnMissingBean
        public RestTemplateCustomizer restTemplateCustomizer(
                final LoadBalancerInterceptor loadBalancerInterceptor) {
            return restTemplate -> {
                List<ClientHttpRequestInterceptor> list = new ArrayList<>(
                        restTemplate.getInterceptors());
                list.add(loadBalancerInterceptor);
                restTemplate.setInterceptors(list);
            };
        }
    }
}

It can be seen on the class annotations @ConditionalOnClass(RestTemplate.class), and @ConditionalOnBean(LoadBalancerClient.class)must have a project in the current RestTemplate instance, must have been initialized LoadBalancerClient implementation class, will be loaded LoadBalancer automatic assembly.

Which LoadBalancerRequestFactory is used to create LoadBalancerRequest, for LoadBalancerInterceptor use (not in low version), LoadBalancerInterceptorConfig maintained a LoadBalancerInterceptor, RestTemplateCustomizer examples.

  • LoadBalancerInterceptor: intercept every HTTP request, the request is load balancing to bind into the Ribbon of Life Cycle
  • RestTemplateCustomizer: Binding LoadBalancerInterceptor interceptors for each RestTemplate

3.2.3 LoadBalancerInterceptor

public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {

    private LoadBalancerClient loadBalancer;
    private LoadBalancerRequestFactory requestFactory;

    public LoadBalancerInterceptor(LoadBalancerClient loadBalancer, LoadBalancerRequestFactory requestFactory) {
        this.loadBalancer = loadBalancer;
        this.requestFactory = requestFactory;
    }

    public LoadBalancerInterceptor(LoadBalancerClient loadBalancer) {
        // for backwards compatibility
        this(loadBalancer, new LoadBalancerRequestFactory(loadBalancer));
    }

    @Override
    public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
            final ClientHttpRequestExecution execution) throws IOException {
        final URI originalUri = request.getURI();
        String serviceName = originalUri.getHost();
        Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri);
        return this.loadBalancer.execute(serviceName, requestFactory.createRequest(request, body, execution));
    }
}

LoadBalancerInterceptor use ClientHttpRequestInterceptorfor each HTTP request to intercept this class is maintained Spring request interceptor. Can be seen, the use of intercepted requests LoadBalancerClientthe execute method of processing request (service name due RestTemplate used as host, so in this case getHosts () acquired service name), LoadBalancerClient only one implementation class: RibbonLoadBalancerClient, particularly the execute method is as follows:

@Override
public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException {
    ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
    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);
}

It can be seen in the source code first gets a LoadBalancer, go get a Server, then this is the specific service instance Server encapsulates. Since Server is a specific service instance, then, getServer (loadBalancer) is the local load balancing occurs.

protected Server getServer(ILoadBalancer loadBalancer) {
    if (loadBalancer == null) {
        return null;
    }
    return loadBalancer.chooseServer("default"); // TODO: better handling of key
}

View chooseServer specific implementation method (BaseLoadBalancer):

public Server chooseServer(Object key) {
    if (counter == null) {
        counter = createCounter();
    }
    counter.increment();
    if (rule == null) {
        return null;
    } else {
        try {
            return rule.choose(key);
        } catch (Exception e) {
            logger.warn("LoadBalancer [{}]:  Error choosing server for key {}", name, key, e);
            return null;
        }
    }
}

rule.choose (key) of the rule, is IRule, and IRule is Ribbon load balancing strategy. It can be proved, HTTP request load balancing policy domain associated up.

3.2.4 IRule

public interface IRule{
    /*
     * choose one alive server from lb.allServers or
     * lb.upServers according to key
     * 
     * @return choosen Server object. NULL is returned if none
     *  server is available 
     */

    public Server choose(Object key);
    
    public void setLoadBalancer(ILoadBalancer lb);
    
    public ILoadBalancer getLoadBalancer();    
}

In IRule we have defined three methods, choose a method to realize implementation class will be added to a particular load balancing policy logic, associated with the other two methods ILoadBalancer.
During the call, Ribbon by ILoadBalancer association IRule, ILoadBalancer of chooseServer method will be converted to invoke choose method IRRule abstract class AbstractLoadBalancerRuleimplements these two methods, which will be associated with IRule ILoadBalancer up.

 

Released eight original articles · won praise 0 · Views 7272

Guess you like

Origin blog.csdn.net/fedorafrog/article/details/104156757