Series of articles:

SpringCloud source series (1) – registry initialization for Eureka

SpringCloud source code series (2) – Registry Eureka service registration, renewal

SpringCloud source code series (3) – Registry Eureka crawl registry

SpringCloud source code series (4) – Registry Eureka service offline, failure, self-protection mechanism

SpringCloud source series (5) – Registry EurekaServer cluster for Eureka

SpringCloud source Code Series (6) – Summary of the Registry Eureka

SpringCloud source series (7) – load balancing Ribbon RestTemplate

SpringCloud source series (8) – load balancing Ribbon core principles

Ribbon Core Interface

Now that you’ve seen how the Ribbon core interface and the default implementation collaborate to find an instance to call, let’s take a look at some of the features and other implementation classes of each core interface.

Client configuration – IClientConfig

IClientConfig is the core interface for managing client configuration, and its default implementation class is DefaultClientConfigImpl. When creating IClientConfig, the Ribbon client sets the default connection and read timeout time to 1 second. For example, if the read time exceeds 1 second, the Ribbon returns a timeout. These two Settings need to be adjusted according to actual conditions.

@Bean
@ConditionalOnMissingBean
public IClientConfig ribbonClientConfig(a) {
    DefaultClientConfigImpl config = new DefaultClientConfigImpl();
    // Load the configuration
    config.loadProperties(this.name);
    // Connection timeout defaults to 1 second
    config.set(CommonClientConfigKey.ConnectTimeout, DEFAULT_CONNECT_TIMEOUT);
    // Read timeout defaults to 1 second
    config.set(CommonClientConfigKey.ReadTimeout, DEFAULT_READ_TIMEOUT);
    config.set(CommonClientConfigKey.GZipPayload, DEFAULT_GZIP_PAYLOAD);
    return config;
}
Copy the code

CommonClientConfigKey This class defines key constants for all configurations associated with the Ribbon client. You can use this class to see which configurations are available.

public abstract class CommonClientConfigKey<T> implements IClientConfigKey<T> {

    public static final IClientConfigKey<String> AppName = new CommonClientConfigKey<String>("AppName") {};public static final IClientConfigKey<Integer> MaxAutoRetries = new CommonClientConfigKey<Integer>("MaxAutoRetries") {};public static final IClientConfigKey<Integer> MaxAutoRetriesNextServer = new CommonClientConfigKey<Integer>("MaxAutoRetriesNextServer") {};public static final IClientConfigKey<Boolean> OkToRetryOnAllOperations = new CommonClientConfigKey<Boolean>("OkToRetryOnAllOperations") {};public static final IClientConfigKey<Integer> ConnectTimeout = new CommonClientConfigKey<Integer>("ConnectTimeout") {};public static final IClientConfigKey<Integer> BackoffInterval = new CommonClientConfigKey<Integer>("BackoffTimeout") {};public static final IClientConfigKey<Integer> ReadTimeout = new CommonClientConfigKey<Integer>("ReadTimeout") {};//LoadBalancer Related
    public static final IClientConfigKey<Boolean> InitializeNFLoadBalancer = new CommonClientConfigKey<Boolean>("InitializeNFLoadBalancer") {};public static final IClientConfigKey<String> NFLoadBalancerClassName = new CommonClientConfigKey<String>("NFLoadBalancerClassName") {};public static final IClientConfigKey<String> NFLoadBalancerRuleClassName = new CommonClientConfigKey<String>("NFLoadBalancerRuleClassName") {};public static final IClientConfigKey<String> NFLoadBalancerPingClassName = new CommonClientConfigKey<String>("NFLoadBalancerPingClassName") {};public static final IClientConfigKey<Integer> NFLoadBalancerPingInterval = new CommonClientConfigKey<Integer>("NFLoadBalancerPingInterval") {};public static final IClientConfigKey<Integer> NFLoadBalancerMaxTotalPingTime = new CommonClientConfigKey<Integer>("NFLoadBalancerMaxTotalPingTime") {};public static final IClientConfigKey<String> NFLoadBalancerStatsClassName = new CommonClientConfigKey<String>("NFLoadBalancerStatsClassName") {};public static final IClientConfigKey<String> NIWSServerListClassName = new CommonClientConfigKey<String>("NIWSServerListClassName") {};public static final IClientConfigKey<String> ServerListUpdaterClassName = new CommonClientConfigKey<String>("ServerListUpdaterClassName") {};public static final IClientConfigKey<String> NIWSServerListFilterClassName = new CommonClientConfigKey<String>("NIWSServerListFilterClassName") {};public static final IClientConfigKey<Integer> ServerListRefreshInterval = new CommonClientConfigKey<Integer>("ServerListRefreshInterval") {};// ...
}
Copy the code

Going into DefaultClientConfigImpl, you can see that each configuration in CommonClientConfigKey corresponds to a default value. When the configuration is loaded, if the user does not customize the configuration, the default configuration is used.

public class DefaultClientConfigImpl implements IClientConfig {

    public static final String DEFAULT_NFLOADBALANCER_PING_CLASSNAME = "com.netflix.loadbalancer.DummyPing"; // DummyPing.class.getName();

    public static final String DEFAULT_NFLOADBALANCER_RULE_CLASSNAME = "com.netflix.loadbalancer.AvailabilityFilteringRule";

    public static final String DEFAULT_NFLOADBALANCER_CLASSNAME = "com.netflix.loadbalancer.ZoneAwareLoadBalancer";

    public static final int DEFAULT_MAX_AUTO_RETRIES_NEXT_SERVER = 1;

    public static final int DEFAULT_MAX_AUTO_RETRIES = 0;

    public static final int DEFAULT_BACKOFF_INTERVAL = 0;

    public static final int DEFAULT_READ_TIMEOUT = 5000;

    public static final int DEFAULT_CONNECTION_MANAGER_TIMEOUT = 2000;

    public static final int DEFAULT_CONNECT_TIMEOUT = 2000;

    public static final String DEFAULT_SEVER_LIST_CLASS = "com.netflix.loadbalancer.ConfigurationBasedServerList";

    public static final String DEFAULT_SERVER_LIST_UPDATER_CLASS = "com.netflix.loadbalancer.PollingServerListUpdater";

    public String getDefaultNfloadbalancerPingClassname(a) {
        return DEFAULT_NFLOADBALANCER_PING_CLASSNAME;
    }

    public String getDefaultNfloadbalancerRuleClassname(a) {
        return DEFAULT_NFLOADBALANCER_RULE_CLASSNAME;
    }

    public String getDefaultNfloadbalancerClassname(a) {
        return DEFAULT_NFLOADBALANCER_CLASSNAME;
    }

    public int getDefaultMaxRetriesPerServerPrimeConnection(a) {
        return DEFAULT_MAX_RETRIES_PER_SERVER_PRIME_CONNECTION;
    }

    public Boolean getDefaultEnablePrimeConnections(a) {
        return DEFAULT_ENABLE_PRIME_CONNECTIONS;
    }

    //....
}
Copy the code

You can also customize the configuration in the configuration file, such as configuring timeout and retry:

# global configuration
ribbon:
  Client read timeout
  ReadTimeout: 3000
  Client connection timeout
  ConnectTimeout: 3000
  If set to true, retry all types, such as POST, PUT, and DELETE
  OkToRetryOnAllOperations: false
  # retries
  MaxAutoRetries: 1
  Retry a maximum of several instances
  MaxAutoRetriesNextServer: 1

# Only for the Demo-producer client
demo-producer:
  ribbon:
    Client read timeout
    ReadTimeout: 5000
    Client connection timeout
    ConnectTimeout: 3000
Copy the code

Balancing policy – IRule

IRule is the policy rule class that ultimately selects the Server. The core interface is Choose.

public interface IRule{
    / / select Server
    public Server choose(Object key);

    / / set ILoadBalancer
    public void setLoadBalancer(ILoadBalancer lb);

    / / get ILoadBalancer
    public ILoadBalancer getLoadBalancer(a);
}
Copy the code

The Ribbon provides a wide range of load balancing policies. You can also configure a load balancing policy. Here are the IRule balancing policies that the Ribbon provides.

Service check – IPing

IPing is used to periodically check the availability of the Server. It provides only one interface to determine whether the Server is alive or not:

public interface IPing {
    public boolean isAlive(Server server);
}
Copy the code

IPing also provides a variety of policies to choose from. Here is the overall IPing architecture:

Get the service list – ServerList

ServerList provides two interfaces, one is to get the Server list for the first time, one is to update the Server list, GetUpdatedListOfServers will be called every 30 seconds by the Loadbalancer to update allServerList.

public interface ServerList<T extends Server> {

    public List<T> getInitialListOfServers(a);

    /** * Return updated list of servers. This is called say every 30 secs * (configurable) by the Loadbalancer's Ping cycle * /
    public List<T> getUpdatedListOfServers(a);
}
Copy the code

ServerList also provides multiple implementations. The ServerList architecture is as follows:

Filtering service – ServerListFilter

ServerListFilter provides an interface to filter out available servers.

public interface ServerListFilter<T extends Server> {
    public List<T> getFilteredListOfServers(List<T> servers);
}
Copy the code

The ServerListFilter architecture is as follows:

Service list update – ServerListUpdater

ServerListUpdater has multiple interfaces. The most important one is to start a scheduled task and call updateAction to update the allServerList.

public interface ServerListUpdater {

    /** * an interface for the updateAction that actually executes a server list update */
    public interface UpdateAction {
        void doUpdate(a);
    }

    /** * start the serverList updater with the given update action * This call should be idempotent. */
    void start(UpdateAction updateAction);
}
Copy the code

There are two implementation classes by default:

Load balancer – ILoadBalancer

ILoadBalancer is the core interface for load balancing service selection. It provides the following interface for obtaining the Server list and selecting the Server based on the client name.

public interface ILoadBalancer {
    / / add a Server
    public void addServers(List<Server> newServers);

    // Select a Server based on the key
    public Server chooseServer(Object key);

    // Get the list of surviving servers, return the upServerList
    public List<Server> getReachableServers(a);

    // Get the list of all servers, return allServerList
    public List<Server> getAllServers(a);
}
Copy the code

The architecture of ILoadBalancer is as follows:

Ribbon configuration classes

There are many configuration classes in the Ribbon. Here we summarize the configuration classes in the Ribbon. We look at the configuration order of each class and what is configured in it.

EurekaClientAutoConfiguration

First is the Eureka EurekaClientAutoConfiguration client configuration class, the main configuration automation configuration class Ribbon EurekaClient as needed.

@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties
@ConditionalOnClass(EurekaClientConfig.class)
@ConditionalOnProperty(value = "eureka.client.enabled", matchIfMissing = true)
@ConditionalOnDiscoveryEnabled
@AutoConfigureBefore({ NoopDiscoveryClientAutoConfiguration.class, CommonsClientAutoConfiguration.class, ServiceRegistryAutoConfiguration.class })
@AutoConfigureAfter(name = { "org.springframework.cloud.netflix.eureka.config.DiscoveryClientOptionalArgsConfiguration",  "org.springframework.cloud.autoconfigure.RefreshAutoConfiguration", "org.springframework.cloud.netflix.eureka.EurekaDiscoveryClientConfiguration", "org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationAutoConfiguration" })
public class EurekaClientAutoConfiguration {
    / /...
}
Copy the code

RibbonAutoConfiguration

The Ribbon automatic configuration class is followed by RibbonAutoConfiguration, which is mainly configured as follows:

  • SpringClientFactory: Manages the Ribbon client context.
  • LoadBalancerClient: load balancing the client, the default implementation class for RibbonLoadBalancerClient (is actually in RibbonClientConfiguration configuration).
  • PropertiesFactory: used to determine whether a configuration file defines the core interface implementation class, such as NFLoadBalancerClassName, NFLoadBalancerPingClassName, etc.
  • RibbonApplicationContextInitializer: This class is used to initialize the Ribbon client context at boot time when hungry configuration is enabled.
package org.springframework.cloud.netflix.ribbon;

@Configuration
@Conditional(RibbonAutoConfiguration.RibbonClassesConditions.class)
@RibbonClients
/ / after EurekaClientAutoConfiguration configuration
@AutoConfigureAfter(name = "org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration")
/ / before LoadBalancerAutoConfiguration, AsyncLoadBalancerAutoConfiguration configuration
@AutoConfigureBefore({ LoadBalancerAutoConfiguration.class, AsyncLoadBalancerAutoConfiguration.class })
@EnableConfigurationProperties({ RibbonEagerLoadProperties.class, ServerIntrospectorProperties.class })
public class RibbonAutoConfiguration {

    @Autowired(required = false)
    private List<RibbonClientSpecification> configurations = new ArrayList<>();

    @Autowired
    private RibbonEagerLoadProperties ribbonEagerLoadProperties;

    @Bean
    public HasFeatures ribbonFeature(a) {
        return HasFeatures.namedFeature("Ribbon", Ribbon.class);
    }

    @Bean
    @ConditionalOnMissingBean
    public SpringClientFactory springClientFactory(a) {
        SpringClientFactory factory = new SpringClientFactory();
        factory.setConfigurations(this.configurations);
        return factory;
    }

    @Bean
    @ConditionalOnMissingBean(LoadBalancerClient.class)
    public LoadBalancerClient loadBalancerClient(a) {
        return new RibbonLoadBalancerClient(springClientFactory());
    }

    @Bean
    @ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")
    @ConditionalOnMissingBean
    public LoadBalancedRetryFactory loadBalancedRetryPolicyFactory(final SpringClientFactory clientFactory) {
        return new RibbonLoadBalancedRetryFactory(clientFactory);
    }

    @Bean
    @ConditionalOnMissingBean
    public PropertiesFactory propertiesFactory(a) {
        return new PropertiesFactory();
    }

    @Bean
    @ConditionalOnProperty("ribbon.eager-load.enabled")
    public RibbonApplicationContextInitializer ribbonApplicationContextInitializer(a) {
        return newRibbonApplicationContextInitializer(springClientFactory(), ribbonEagerLoadProperties.getClients()); }}Copy the code

LoadBalancerAutoConfiguration

Then the load balancer configuration class LoadBalancerAutoConfiguration, this class is to create a load balancing interceptor LoadBalancerInterceptor, and added to the RestTemplae interceptors.

package org.springframework.cloud.client.loadbalancer;

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RestTemplate.class)
@ConditionalOnBean(LoadBalancerClient.class)
@EnableConfigurationProperties(LoadBalancerRetryProperties.class)
public class LoadBalancerAutoConfiguration {

    @LoadBalanced
    @Autowired(required = false)
    private List<RestTemplate> restTemplates = Collections.emptyList();
    @Autowired(required = false)
    private List<LoadBalancerRequestTransformer> transformers = Collections.emptyList();

    // Customize the RestTemplate
    @Bean
    public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(
            final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {
        return () -> restTemplateCustomizers.ifAvailable(customizers -> {
            for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
                for(RestTemplateCustomizer customizer : customizers) { customizer.customize(restTemplate); }}}); }@Bean
    @ConditionalOnMissingBean
    public LoadBalancerRequestFactory loadBalancerRequestFactory(LoadBalancerClient loadBalancerClient) {
        return new LoadBalancerRequestFactory(loadBalancerClient, this.transformers);
    }

    @Configuration(proxyBeanMethods = false)
    @ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
    static class LoadBalancerInterceptorConfig {

        // Create RestTemplate interceptor
        @Bean
        public LoadBalancerInterceptor ribbonInterceptor(LoadBalancerClient loadBalancerClient, LoadBalancerRequestFactory requestFactory) {
            return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
        }

        // RestTemplate customizer
        @Bean
        @ConditionalOnMissingBean
        public RestTemplateCustomizer restTemplateCustomizer(final LoadBalancerInterceptor loadBalancerInterceptor) {
            return restTemplate -> {
                List<ClientHttpRequestInterceptor> list = newArrayList<>(restTemplate.getInterceptors()); list.add(loadBalancerInterceptor); restTemplate.setInterceptors(list); }; }}}Copy the code

RibbonClientConfiguration

After is the default Ribbon RibbonClientConfiguration client configuration class, this class is mainly configured with Ribbon core the default implementation of the interface.

  • IClientConfig: Ribbon client configuration class. Default implementation is DefaultClientConfigImpl.
  • IRule: Load balancing policy rule component, the default implementation is ZoneAvoidanceRule.
  • IPingThe default implementation is DummyPing, which always returns true.
  • ServerListComponents: access to the Server, the default implementation class for ConfigurationBasedServerList, obtained from the config file.
  • ServerListUpdater: Server list update component, default implementation class is PollingServerListUpdater.
  • ServerListFilter: filter available in the Server list, the default implementation class for ZonePreferenceServerListFilter.
  • RibbonLoadBalancerContext: load balancing client.
  • RetryHandler: retry processor, the default implementation class for DefaultLoadBalancerRetryHandler.
package org.springframework.cloud.netflix.ribbon;

@SuppressWarnings("deprecation")
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties
@Import({ HttpClientConfiguration.class, OkHttpRibbonConfiguration.class, RestClientRibbonConfiguration.class, HttpClientRibbonConfiguration.class })
public class RibbonClientConfiguration {
    public static final int DEFAULT_CONNECT_TIMEOUT = 1000;
    public static final int DEFAULT_READ_TIMEOUT = 1000;
    public static final boolean DEFAULT_GZIP_PAYLOAD = true;

    @RibbonClientName
    private String name = "client";
    @Autowired
    private PropertiesFactory propertiesFactory;

    @Bean
    @ConditionalOnMissingBean
    public IClientConfig ribbonClientConfig(a) {
        DefaultClientConfigImpl config = new DefaultClientConfigImpl();
        config.loadProperties(this.name);
        config.set(CommonClientConfigKey.ConnectTimeout, DEFAULT_CONNECT_TIMEOUT);
        config.set(CommonClientConfigKey.ReadTimeout, DEFAULT_READ_TIMEOUT);
        config.set(CommonClientConfigKey.GZipPayload, DEFAULT_GZIP_PAYLOAD);
        return config;
    }

    @Bean
    @ConditionalOnMissingBean
    public IRule ribbonRule(IClientConfig config) {
        if (this.propertiesFactory.isSet(IRule.class, name)) {
            return this.propertiesFactory.get(IRule.class, config, name);
        }
        ZoneAvoidanceRule rule = new ZoneAvoidanceRule();
        rule.initWithNiwsConfig(config);
        return rule;
    }

    @Bean
    @ConditionalOnMissingBean
    public IPing ribbonPing(IClientConfig config) {
        if (this.propertiesFactory.isSet(IPing.class, name)) {
            return this.propertiesFactory.get(IPing.class, config, name);
        }
        return new DummyPing();
    }

    @Bean
    @ConditionalOnMissingBean
    @SuppressWarnings("unchecked")
    public ServerList<Server> ribbonServerList(IClientConfig config) {
        if (this.propertiesFactory.isSet(ServerList.class, name)) {
            return this.propertiesFactory.get(ServerList.class, config, name);
        }
        ConfigurationBasedServerList serverList = new ConfigurationBasedServerList();
        serverList.initWithNiwsConfig(config);
        return serverList;
    }

    @Bean
    @ConditionalOnMissingBean
    public ServerListUpdater ribbonServerListUpdater(IClientConfig config) {
        return new PollingServerListUpdater(config);
    }

    @Bean
    @ConditionalOnMissingBean
    public ILoadBalancer ribbonLoadBalancer(IClientConfig config, ServerList
       
         serverList, ServerListFilter
        
          serverListFilter, IRule rule, IPing ping, ServerListUpdater serverListUpdater)
        
        {
        if (this.propertiesFactory.isSet(ILoadBalancer.class, name)) {
            return this.propertiesFactory.get(ILoadBalancer.class, config, name);
        }
        return new ZoneAwareLoadBalancer<>(config, rule, ping, serverList,
                serverListFilter, serverListUpdater);
    }

    @Bean
    @ConditionalOnMissingBean
    @SuppressWarnings("unchecked")
    public ServerListFilter<Server> ribbonServerListFilter(IClientConfig config) {
        if (this.propertiesFactory.isSet(ServerListFilter.class, name)) {
            return this.propertiesFactory.get(ServerListFilter.class, config, name);
        }
        ZonePreferenceServerListFilter filter = new ZonePreferenceServerListFilter();
        filter.initWithNiwsConfig(config);
        return filter;
    }

    @Bean
    @ConditionalOnMissingBean
    public RibbonLoadBalancerContext ribbonLoadBalancerContext(ILoadBalancer loadBalancer, IClientConfig config, RetryHandler retryHandler) {
        return new RibbonLoadBalancerContext(loadBalancer, config, retryHandler);
    }

    @Bean
    @ConditionalOnMissingBean
    public RetryHandler retryHandler(IClientConfig config) {
        return new DefaultLoadBalancerRetryHandler(config);
    }

    @Bean
    @ConditionalOnMissingBean
    public ServerIntrospector serverIntrospector(a) {
        return newDefaultServerIntrospector(); }}Copy the code

RibbonEurekaAutoConfiguration

Ribbon Eureka automation configuration class RibbonEurekaAutoConfiguration, judge whether to enable Ribbon Eureka, triggering EurekaRibbonClientConfiguration configuration class.

package org.springframework.cloud.netflix.ribbon.eureka;

@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties
@ConditionalOnRibbonAndEurekaEnabled
@AutoConfigureAfter(RibbonAutoConfiguration.class)
@RibbonClients(defaultConfiguration = EurekaRibbonClientConfiguration.class)
public class RibbonEurekaAutoConfiguration {}Copy the code

EurekaRibbonClientConfiguration

Enabled by default Ribbon under the condition of Eureka, will use the Ribbon Eureka EurekaRibbonClientConfiguration client configuration class:

  • IPing: The default implementation class DummyPing is replaced by NIWSDiscoveryPing, which determines whether the Server is alive by checking whether the InstanceInfo status is UP.
  • ServerList: Replace the default implementation class ConfigurationBasedServerList, DomainExtractingServerList instead, actual it is DiscoveryEnabledNIWSServerList, Get the Server list from EurekaClient.
package org.springframework.cloud.netflix.ribbon.eureka;

@Configuration(proxyBeanMethods = false)
public class EurekaRibbonClientConfiguration {

    @Value("${ribbon.eureka.approximateZoneFromHostname:false}")
    private boolean approximateZoneFromHostname = false;
    @RibbonClientName
    private String serviceId = "client";
    @Autowired(required = false)
    private EurekaClientConfig clientConfig;
    @Autowired(required = false)
    private EurekaInstanceConfig eurekaConfig;
    @Autowired
    private PropertiesFactory propertiesFactory;

    public EurekaRibbonClientConfiguration(a) {}public EurekaRibbonClientConfiguration(EurekaClientConfig clientConfig, String serviceId, EurekaInstanceConfig eurekaConfig, boolean approximateZoneFromHostname) {
        this.clientConfig = clientConfig;
        this.serviceId = serviceId;
        this.eurekaConfig = eurekaConfig;
        this.approximateZoneFromHostname = approximateZoneFromHostname;
    }

    @Bean
    @ConditionalOnMissingBean
    public IPing ribbonPing(IClientConfig config) {
        if (this.propertiesFactory.isSet(IPing.class, serviceId)) {
            return this.propertiesFactory.get(IPing.class, config, serviceId);
        }
        NIWSDiscoveryPing ping = new NIWSDiscoveryPing();
        ping.initWithNiwsConfig(config);
        return ping;
    }

    @Bean
    @ConditionalOnMissingBean
    publicServerList<? > ribbonServerList(IClientConfig config, Provider<EurekaClient> eurekaClientProvider) {if (this.propertiesFactory.isSet(ServerList.class, serviceId)) {
            return this.propertiesFactory.get(ServerList.class, config, serviceId);
        }
        DiscoveryEnabledNIWSServerList discoveryServerList = new DiscoveryEnabledNIWSServerList(config, eurekaClientProvider);
        DomainExtractingServerList serverList = new DomainExtractingServerList(discoveryServerList, config, this.approximateZoneFromHostname);
        returnserverList; }}Copy the code

The module to which each configuration class belongs

Spring – the cloud – netflix – eureka – the client:

  • org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration
  • org.springframework.cloud.netflix.ribbon.eureka.RibbonEurekaAutoConfiguration
  • org.springframework.cloud.netflix.ribbon.eureka.EurekaRibbonClientConfiguration

Spring – the cloud – netflix – ribbon:

  • org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration
  • org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration

Spring – the cloud – Commons:

  • org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration

Client Ribbon customization

IRule, IPing, created in the RibbonClientConfiguration ServerList < Server >, ServerListFilter < Server > ILoadBalancer, IsSet check whether an implementation class of the corresponding type has been configured. If no implementation class of the corresponding type has been configured, the default implementation class is used.

That is, for a particular service, these classes can be customized or configured to specify other implementation classes. In this section we take a look at how the Ribbon customizes some configurations.

The client Ribbon provides three configurations: Global policy, Annotation based, and Configuration file. The priorities of these configurations are As follows: Configuration File > @RibbonClients Annotation > Global > Default.

Global Policy Configuration

If you want to change the configuration globally, you need to add a configuration class like this:

@Configuration
public class GlobalRibbonConfiguration {

    @Bean
    public IRule ribbonRule(a) {
        return new RandomRule();
    }

    @Bean
    public IPing ribbonPing(a) {
        return newNoOpPing(); }}Copy the code

Annotation-based configuration

If you want to customize the configuration for a service, you can configure a service specific configuration class by using @RibbonClients.

We need to define a service configuration class:

@Configuration
public class ProducerRibbonConfiguration {

    @Bean
    public IRule ribbonRule(a) {
        return new RandomRule();
    }

    @Bean
    public IPing ribbonPing(a) {
        return newNoOpPing(); }}Copy the code

Use the @RibbonClients annotation to specify a specific configuration class for the service, and remove it side by side so that Spring cannot scan it, otherwise it becomes a global configuration.

@RibbonClients({ @RibbonClient(name = "demo-producer", configuration = ProducerRibbonConfiguration.class) })
@ComponentScan(excludeFilters = { @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = ProducerRibbonConfiguration.class) })
public class XxxConfiguration {}Copy the code

Configuration file Configuration

The configuration file is in the format of < service name >. Ribbon.< properties > :

demo-producer:
  ribbon:
    # ILoadBalancer
    NFLoadBalancerClassName: com.netflix.loadbalancer.NoOpLoadBalancer
    # IRule
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
    # IPing
    NFLoadBalancerPingClassName:
    # ServerList<Server>
    NIWSServerListClassName:
    # ServerListFilter<Server>
    NIWSServerListFilterClassName:
Copy the code

The Ribbon context hunger loads

As mentioned in the previous article, when components such as ILoadBalancer are retrieved from SpringClientFactory, they are created at run time. The Ribbon client application context is lazily loaded by default. The context is not loaded at startup, but initialized at the first call.

If you want the Ribbon client to be initialized at startup, you can specify the Ribbon client name and load the context of the configuration item at startup:

ribbon:
  eager-load:
    enabled: true
    Understand the initialized client name at startup
    clients: demo-producer,demo-xxx
Copy the code

In RibbonAutoConfiguration configuration classes can be found in the configuration, the hunger if opens the hunger to load, will create RibbonApplicationContextInitializer to initialized at startup context.

@Bean
@ConditionalOnProperty("ribbon.eager-load.enabled")
public RibbonApplicationContextInitializer ribbonApplicationContextInitializer(a) {
    return new RibbonApplicationContextInitializer(springClientFactory(), ribbonEagerLoadProperties.getClients());
}

public class RibbonApplicationContextInitializer implements ApplicationListener<ApplicationReadyEvent> {
    private final SpringClientFactory springClientFactory;
    // List of Ribbon client names
    private final List<String> clientNames;

    public RibbonApplicationContextInitializer(SpringClientFactory springClientFactory, List<String> clientNames) {
        this.springClientFactory = springClientFactory;
        this.clientNames = clientNames;
    }

    protected void initialize(a) {
        if(clientNames ! =null) {
            for (String clientName : clientNames) {
                // Initialize the context in advance
                this.springClientFactory.getContext(clientName); }}}@Override
    public void onApplicationEvent(ApplicationReadyEvent event) { initialize(); }}Copy the code

The Ribbon is used independently of Eureka

By default, the Ribbon client retrieves the service list from EurekaClient. The Ribbon client indirectly reads the service registry from EurekaClient for dynamic load balancing. However, if you don’t want to read from EurekaClient, you can disable the Eureka feature in the Ribbon. You can disable Eureka in the Ribbon as follows:

ribbon:
  eureka:
    enabled: false
Copy the code

How does ribbon. Eureka. Enabled disable Eureka? See RibbonEurekaAutoConfiguration:

@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties
@ConditionalOnRibbonAndEurekaEnabled
@AutoConfigureAfter(RibbonAutoConfiguration.class)
@RibbonClients(defaultConfiguration = EurekaRibbonClientConfiguration.class)
public class RibbonEurekaAutoConfiguration {}Copy the code

This configuration class by @ RibbonClients specifies the default client configuration class for EurekaRibbonClientConfiguration, but the effect is the premise of @ ConditionalOnRibbonAndEurekaEnabled, If Ribbon Eureka is enabled, set Ribbon. Eureka. Enabled =false to disable Ribbon Eureka.

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
Class OnRibbonAndEurekaEnabledCondition / / conditions
@Conditional(ConditionalOnRibbonAndEurekaEnabled.OnRibbonAndEurekaEnabledCondition.class)
public @interface ConditionalOnRibbonAndEurekaEnabled {

    class OnRibbonAndEurekaEnabledCondition extends AllNestedConditions {

        OnRibbonAndEurekaEnabledCondition() {
            super(ConfigurationPhase.REGISTER_BEAN);
        }

        / / introduces categories: DiscoveryEnabledNIWSServerList
        @ConditionalOnClass(DiscoveryEnabledNIWSServerList.class)
        // There is a bean object: SpringClientFactory
        @ConditionalOnBean(SpringClientFactory.class)
        // ribbon.eureka.enabled=true
        @ConditionalOnProperty(value = "ribbon.eureka.enabled", matchIfMissing = true)
        static class Defaults {}// There is a bean object: EurekaClient
        @ConditionalOnBean(EurekaClient.class)
        static class EurekaBeans {}// eureka.client.enabled=true
        @ConditionalOnProperty(value = "eureka.client.enabled", matchIfMissing = true)
        @ConditionalOnDiscoveryEnabled
        static class OnEurekaClientEnabled {}}}Copy the code

If you want to get the list of services from other places, you can customize the interface ServerList

to get it, or you can set the address list in the configuration file:

<client-name>:
  ribbon:
    listOfServers: http://10.215.0.92:8010,http://10.215.0.92:8011
Copy the code