This article builds on the previous article and continues to analyze the core of the Ribbon. For those who are not familiar with the Ribbon’s principles, take a look at some of the Ribbon’s core operations that SpringBoot automates

RibbonClientConfiguration

RibbonClientConfiguration is a very the Ribbon configuration class, first launched in Ribbon request will be completed when the corresponding initialization. Several related default Settings are completed.

interface The default implementation describe
IClientConfig DefaultClientConfigImpl Management Configuration Interface
IRule ZoneAvoidanceRule Balancing Policy interface
IPing DummyPing Check the service availability interface
ServerList<Server> ConfigurationBasedServerList Gets the service list interface
ILoadBalancer ZoneAwareLoadBalancer Load balancing interface
ServerListUpdater PollingServerListUpdater Periodically update service list interface
ServerIntrospector DefaultServerIntrospector Security port interface
@Bean
    @ConditionalOnMissingBean
    public IClientConfig ribbonClientConfig(a) {
        DefaultClientConfigImpl config = new DefaultClientConfigImpl();
        config.loadProperties(this.name);
        config.set(CommonClientConfigKey.ConnectTimeout, 1000);
        config.set(CommonClientConfigKey.ReadTimeout, 1000);
        config.set(CommonClientConfigKey.GZipPayload, true);
        return config;
    }

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

    @Bean
    @ConditionalOnMissingBean
    public ServerList<Server> ribbonServerList(IClientConfig config) {
        if (this.propertiesFactory.isSet(ServerList.class, this.name)) {
            return (ServerList)this.propertiesFactory.get(ServerList.class, config, this.name);
        } else {
            ConfigurationBasedServerList serverList = new ConfigurationBasedServerList();
            serverList.initWithNiwsConfig(config);
            returnserverList; }}@Bean
    @ConditionalOnMissingBean
    public ServerListUpdater ribbonServerListUpdater(IClientConfig config) {
        return new PollingServerListUpdater(config);
    }

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

One of the more important default implementations is the implementation of the ILoadBalancer object. The implementation of ZoneAwareLoadBalancer. Schematic diagram of implementation:In ZoneAwareLoadBalancer, you have completed the configuration of dynamic service address acquisition and scheduled service address update tasks. It will first enter the ZoneAwareLoadBalancer constructor

public ZoneAwareLoadBalancer(IClientConfig clientConfig, IRule rule, IPing ping, ServerList
       
         serverList, ServerListFilter
        
          filter, ServerListUpdater serverListUpdater)
        
        {
        super(clientConfig, rule, ping, serverList, filter, serverListUpdater);
    }
Copy the code

You can see from the source that the constructor in the parent class is called.

    public DynamicServerListLoadBalancer(IClientConfig clientConfig, IRule rule, IPing ping, ServerList
       
         serverList, ServerListFilter
        
          filter, ServerListUpdater serverListUpdater)
        
        {
        // Continue calling the method in the parent class
        super(clientConfig, rule, ping);
        this.serverListImpl = serverList;
        this.filter = filter;
        this.serverListUpdater = serverListUpdater;
        if (filter instanceof AbstractServerListFilter) {
            ((AbstractServerListFilter) filter).setLoadBalancerStats(getLoadBalancerStats());
        }
        // Complete the relevant initial operation service address fetching and updating
        restOfInit(clientConfig);
    }
Copy the code

In the above source we continue to trace the methods in the parent class.

    void initWithConfig(IClientConfig clientConfig, IRule rule, IPing ping, LoadBalancerStats stats) {
        this.config = clientConfig;
        String clientName = clientConfig.getClientName();
        this.name = clientName;
        // Set the interval of scheduled tasks to 30 seconds.
        int pingIntervalTime = Integer.parseInt(""
                + clientConfig.getProperty(
                        CommonClientConfigKey.NFLoadBalancerPingInterval,
                        Integer.parseInt("30")));
        int maxTotalPingTime = Integer.parseInt(""
                + clientConfig.getProperty(
                        CommonClientConfigKey.NFLoadBalancerMaxTotalPingTime,
                        Integer.parseInt("2")));

        setPingInterval(pingIntervalTime);
        setMaxTotalPingTime(maxTotalPingTime);

        // cross associate with each other
        // i.e. Rule,Ping meet your container LB
        // LB, these are your Ping and Rule guys ...
        setRule(rule);
        setPing(ping);

        setLoadBalancerStats(stats);
        rule.setLoadBalancer(this);
        if (ping instanceof AbstractLoadBalancerPing) {
            ((AbstractLoadBalancerPing) ping).setLoadBalancer(this);
        }
        logger.info("Client: {} instantiated a LoadBalancer: {}", name, this);
        boolean enablePrimeConnections = clientConfig.get(
                CommonClientConfigKey.EnablePrimeConnections, DefaultClientConfigImpl.DEFAULT_ENABLE_PRIME_CONNECTIONS);

        if (enablePrimeConnections) {
            this.setEnablePrimingConnections(true);
            PrimeConnections primeConnections = new PrimeConnections(
                    this.getName(), clientConfig);
            this.setPrimeConnections(primeConnections);
        }
        init();

    }
Copy the code

The comparison in the initWithConfig method is to set the interval for scheduled tasks. And then we go back to the restOfInit method. (Let’s advance: 463257262)

    void restOfInit(IClientConfig clientConfig) {
        boolean primeConnection = this.isEnablePrimingConnections();
        // turn this off to avoid duplicated asynchronous priming done in BaseLoadBalancer.setServerList()
        this.setEnablePrimingConnections(false);
        // Set a scheduled task
        enableAndInitLearnNewServersFeature();
		// Get and update the service address
        updateListOfServers();
        if (primeConnection && this.getPrimeConnections() ! =null) {
            this.getPrimeConnections()
                    .primeConnections(getReachableServers());
        }
        this.setEnablePrimingConnections(primeConnection);
        LOGGER.info("DynamicServerListLoadBalancer for client {} initialized: {}", clientConfig.getClientName(), this.toString());
    }
Copy the code

See first enableAndInitLearnNewServersFeature method

    public void enableAndInitLearnNewServersFeature(a) {
        LOGGER.info("Using serverListUpdater {}", serverListUpdater.getClass().getSimpleName());
        serverListUpdater.start(updateAction);
    }
Copy the code

There are many different implementations of the start method, just choose the corresponding option based on our service. Such as local use PollingServerListUpdater, if is had the choice of the EurekaNotificationServerListUpdater registry.Take local as an example:

@Override
    public synchronized void start(final UpdateAction updateAction) {
        if (isActive.compareAndSet(false.true)) {
            // The task body of the scheduled task
            final Runnable wrapperRunnable = new Runnable() {
                @Override
                public void run(a) {
                    if(! isActive.get()) {if(scheduledFuture ! =null) {
                            scheduledFuture.cancel(true);
                        }
                        return;
                    }
                    try {
                        // The method body of doUpdate() should be careful
                        updateAction.doUpdate();
                        lastUpdated = System.currentTimeMillis();
                    } catch (Exception e) {
                        logger.warn("Failed one update cycle", e); }}};// Set the scheduled task 10 seconds to start the first check. The interval is 30 seconds
            scheduledFuture = getRefreshExecutor().scheduleWithFixedDelay(
                    wrapperRunnable,
                    initialDelayMs,
                    refreshIntervalMs,
                    TimeUnit.MILLISECONDS
            );
        } else {
            logger.info("Already active, no-op"); }}Copy the code

This section uses the local task as an example.So the method for the scheduled task execution is the [updateListOfServers] method, which is:emsp; So let’s go ahead and look at the logic in the updateListOfServers method

    @VisibleForTesting
    public void updateListOfServers(a) {
        List<T> servers = new ArrayList<T>();
        if(serverListImpl ! =null) {
            // Obtain the service address information from the local or Eureka or Nacos configuration center
            servers = serverListImpl.getUpdatedListOfServers();
            LOGGER.debug("List of Servers for {} obtained from Discovery client: {}",
                    getIdentifier(), servers);

            if(filter ! =null) {
                servers = filter.getFilteredListOfServers(servers);
                LOGGER.debug("Filtered List of Servers for {} obtained from Discovery client: {}", getIdentifier(), servers); }}// Update service address information
        updateAllServerList(servers);
    }
Copy the code

The important methods in the above code are getUpdatedListOfServers and updateAllServerList. Let’s look at the getUpdatedListOfServers method firstView local logic, Eureka’s own view

	@Override
	public List<Server> getUpdatedListOfServers(a) {
        // Obtain it from the local configuration
        String listOfServers = clientConfig.get(CommonClientConfigKey.ListOfServers);
        return derive(listOfServers);
	}
Copy the code

And then there’s the updateAllServerList method

    protected void updateAllServerList(List<T> ls) {
        // other threads might be doing this - in which case, we pass
        // CAS ensures atomicity of operations
        if (serverListUpdateInProgress.compareAndSet(false.true)) {
            try {
                for (T s : ls) {
                    s.setAlive(true); // set so that clients can start using these
                                      // servers right away instead
                                      // of having to wait out the ping cycle.
                }
                // Update service address information
                setServersList(ls);
                // Ping the service address forcibly
                super.forceQuickPing();
            } finally {
                serverListUpdateInProgress.set(false); }}}Copy the code

The above operation flow chart is as follows:

Ok ~ [RibbonClientConfiguration] the contents of this configuration class is to introduce you to here, welcome to a key three even!!!!!!