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!!!!!!