DyamicServerListLoadBalancer的构造方法中,当接收到的参数为IClientConfig的时候,会直接调用initWithNiwsConfig()方法开始根据配置类开始进行初始化。
首先会调用父类BaseLoaderBalancer的initWithNiwsConfig()方法。
在BaseLoaderBalancer这里首先会在配置类中寻找相应的IPing和IRule并获取这两个类相应的实例,而后在initWithConfig()方法中对这两者以及相关属性进行更详细的配置。
void initWithConfig(IClientConfig clientConfig, IRule rule, IPing ping) {
this.config = clientConfig;
String clientName = clientConfig.getClientName();
this.name = clientName;
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(new LoadBalancerStats(clientName));
rule.setLoadBalancer(this);
if (ping instanceof AbstractLoadBalancerPing) {
((AbstractLoadBalancerPing) ping).setLoadBalancer(this);
}
logger.info("Client:" + name + " instantiated a LoadBalancer:"
+ toString());
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();
}
先在配置类中读取ping操作的间隔,默认30秒。然后在setPingInterval()方法中将这一参数正式用到。在setPingInterval()方法中,会调用setupPingTask()方法正式根据时间间隔创建定时任务PingTask来不断间隔一定时间去对所有地址进行ping检查。
private void setupPingTask() {
if (canSkipPing()) {
return;
}
if (lbTimer != null) {
lbTimer.cancel();
}
lbTimer = new ShutdownEnabledTimer("NFLoadBalancer-PingTimer-" + name,
true);
lbTimer.schedule(new PingTask(), 0, pingIntervalSeconds * 1000);
forceQuickPing();
}
class PingTask extends TimerTask {
public void run() {
Pinger ping = new Pinger();
try {
ping.runPinger();
} catch (Throwable t) {
logger.error("Throwable caught while running extends for "
+ name, t);
}
}
}
public void runPinger() {
if (pingInProgress.get()) {
return; // Ping in progress - nothing to do
} else {
pingInProgress.set(true);
}
// we are "in" - we get to Ping
Object[] allServers = null;
boolean[] results = null;
Lock allLock = null;
Lock upLock = null;
try {
/*
* The readLock should be free unless an addServer operation is
* going on...
*/
allLock = allServerLock.readLock();
allLock.lock();
allServers = allServerList.toArray();
allLock.unlock();
int numCandidates = allServers.length;
results = new boolean[numCandidates];
if (logger.isDebugEnabled()) {
logger.debug("LoadBalancer: PingTask executing ["
+ numCandidates + "] servers configured");
}
for (int i = 0; i < numCandidates; i++) {
results[i] = false; /* Default answer is DEAD. */
try {
// NOTE: IFF we were doing a real ping
// assuming we had a large set of servers (say 15)
// the logic below will run them serially
// hence taking 15 times the amount of time it takes
// to ping each server
// A better method would be to put this in an executor
// pool
// But, at the time of this writing, we dont REALLY
// use a Real Ping (its mostly in memory eureka call)
// hence we can afford to simplify this design and run
// this
// serially
if (ping != null) {
results[i] = ping.isAlive((Server) allServers[i]);
}
} catch (Throwable t) {
logger.error("Exception while pinging Server:"
+ allServers[i], t);
}
}
final List<Server> newUpList = new ArrayList<Server>();
final List<Server> changedServers = new ArrayList<Server>();
for (int i = 0; i < numCandidates; i++) {
boolean isAlive = results[i];
Server svr = (Server) allServers[i];
boolean oldIsAlive = svr.isAlive();
svr.setAlive(isAlive);
if (oldIsAlive != isAlive) {
changedServers.add(svr);
if (logger.isDebugEnabled()) {
logger.debug("LoadBalancer: Server [" + svr.getId()
+ "] status changed to "
+ (isAlive ? "ALIVE" : "DEAD"));
}
}
if (isAlive) {
newUpList.add(svr);
}
}
// System.out.println(count + " servers alive");
upLock = upServerLock.writeLock();
upLock.lock();
upServerList = newUpList;
upLock.unlock();
notifyServerStatusChangeListener(changedServers);
} catch (Throwable t) {
logger.error("Throwable caught while running the Pinger-"
+ name, t);
} finally {
pingInProgress.set(false);
}
}
在ping检查中,最后调用到的是Pinger的runPinger()方法。
为了保证这一操作的线程安全,通过信号量的形式保证这一操作在某一时间的唯一性。
而后会获取所有当前所有的服务器数组,遍历数组去依次进行ping确认其状态是否可用,如果新的状态与之前的状态发生改变,那么就将其存入存放状态发生改变的服务器数组,并且如果其状态为可用,则加入可用服务器数组并更新。
而后通知所有状态改变监听器公告所有发生状态改变的服务器。
如果采用了PingUrl会直接构造url对相应的服务器进行请求来判断该服务器是否可用。
该任务的定时调用间隔时间就是配置的ping间隔时间。
之后回到DyamicServerListLoadBalancer的initWithNiwConfig()方法,会配置服务器列表和服务器过滤器。
而后调用restOfInit()方法,并在其中调用updateListOfServers()方法更新服务器列表。
public void updateListOfServers() {
List<T> servers = new ArrayList<T>();
if (serverListImpl != null) {
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);
}
}
lastUpdated.set(System.currentTimeMillis());
updateAllServerList(servers);
}
这里会根据之前得到的服务器列表类来获取服务器列表。
如果是默认的ConfigurationBasedServerList,则会直接读配置类中的服务器列表并返回。
而如果采用了DiscoveryEnabledNIWSServerList,则向eureka-client获得相应的服务器列表。