前言
我们知道Ribbon的负载均衡功能 最主要的功能就是对服务器列表的维护,我们前面也说到 ServerList
用来提供一个服务器的列表、ServerListFilter
用来过滤服务列表,今天我们再来看一个更服务列表有关的组件 ServerListUpdater
ServerListUpdater继承关系
ServerListUpdater
在Ribbon中只有两个实现。其中EurekaNotificationServerListUpdater
和Eureka有关 此处不作介绍。
我们即可认为PollingServerListUpdater
在这里是默认且唯一对ServerListUpdater
的实现。
ServerListUpdater接口方法介绍
public interface UpdateAction {
void doUpdate();
}
void start(UpdateAction updateAction);
void stop();
String getLastUpdate();
long getDurationSinceLastUpdateMs();
int getNumberMissedCycles();
int getCoreThreads();
UpdateAction:
内部接口 一般会以函数接口的方式来使用。具体更新的动作,所以从这里可以看出ServerListUpdater
它只是相当于一个调度器 维护了调度器的策略而已。而真正的调度的执行的动作是UpdateAction
start:
使用具体的调度策略 开启一个调度任务。stop:
停止服务列表更新器运行getLastUpdate:
获取最后一次更新时间getDurationSinceLastUpdateMs:
自上次更新以来的毫秒数getNumberMissedCycles:
错过更新周期的数量getCoreThreads:
核心线程数量
PollingServerListUpdater
PollingServerListUpdater
是现阶段唯一对ServerListUpdater
的实现。既然是调度器 那么核心就是对线程池的实现设置等操作。
单例线程池的创建
PollingServerListUpdater
创建单例的方式是通过内部类静态域的加载的方式来创建。
private static class LazyHolder {
private final static String CORE_THREAD = "DynamicServerListLoadBalancer.ThreadPoolSize";
private final static DynamicIntProperty poolSizeProp = new DynamicIntProperty(CORE_THREAD, 2);
private static Thread _shutdownThread;
static ScheduledThreadPoolExecutor _serverListRefreshExecutor = null;
static {
int coreSize = poolSizeProp.get();
ThreadFactory factory = (new ThreadFactoryBuilder())
.setNameFormat("PollingServerListUpdater-%d")
.setDaemon(true)
.build();
_serverListRefreshExecutor = new ScheduledThreadPoolExecutor(coreSize, factory);
poolSizeProp.addCallback(new Runnable() {
@Override
public void run() {
_serverListRefreshExecutor.setCorePoolSize(poolSizeProp.get());
}
});
_shutdownThread = new Thread(new Runnable() {
public void run() {
logger.info("Shutting down the Executor Pool for PollingServerListUpdater");
shutdownExecutorPool();
}
});
Runtime.getRuntime().addShutdownHook(_shutdownThread);
}
private static void shutdownExecutorPool() {
if (_serverListRefreshExecutor != null) {
_serverListRefreshExecutor.shutdown();
if (_shutdownThread != null) {
try {
Runtime.getRuntime().removeShutdownHook(_shutdownThread);
} catch (IllegalStateException ise) {
}
}
}
}
}
DynamicServerListLoadBalancer.ThreadPoolSize
配置调度器核心线程数量,默认2个。并且支持动态的更新核心线程数
成员属性
private static long LISTOFSERVERS_CACHE_UPDATE_DELAY = 1000;
private static int LISTOFSERVERS_CACHE_REPEAT_INTERVAL = 30 * 1000;
private final AtomicBoolean isActive = new AtomicBoolean(false);
private volatile long lastUpdated = System.currentTimeMillis();
private final long initialDelayMs;
private final long refreshIntervalMs;
private volatile ScheduledFuture<?> scheduledFuture;
isActive:
当前调度器任务是否是活跃状态中 只有调用start()
方法才会使调度器变成活跃状态。lastUpdated:
任务调用执行一次updateAction.doUpdate()
后记录该initialDelayMs:
默认值是LISTOFSERVERS_CACHE_UPDATE_DELAY
也就是延迟1000ms开始执行refreshIntervalMs:
默认值是LISTOFSERVERS_CACHE_REPEAT_INTERVAL
也就是30s执行一次,可以通过<clientName>.ribbon.ServerListRefreshInterval
来配置刷新时间。scheduledFuture
调度结果
start方法
@Override
public synchronized void start(final UpdateAction updateAction) {
if (isActive.compareAndSet(false, true)) {
final Runnable wrapperRunnable = new Runnable() {
@Override
public void run() {
if (!isActive.get()) {
if (scheduledFuture != null) {
scheduledFuture.cancel(true);
}
return;
}
try {
updateAction.doUpdate();
lastUpdated = System.currentTimeMillis();
} catch (Exception e) {
logger.warn("Failed one update cycle", e);
}
}
};
scheduledFuture = getRefreshExecutor().scheduleWithFixedDelay(
wrapperRunnable,
initialDelayMs,
refreshIntervalMs,
TimeUnit.MILLISECONDS
);
} else {
logger.info("Already active, no-op");
}
}
上面的方法很简单,就是启动一个任务调度器 但是条件是isActive
为false的时候才能启动。
其他的方法就不单独介绍了,整体来说 ServerListUpdater
还是比较简单的。主要的更新逻辑还是在UpdateAction
中来实现的。
总结
服务列表的更新策略 和Archaius的动态配置的调度策略有点类似 可以对比 AbstractPollingScheduler
。一般来说ServerListUpdater
是和ServerList
,ServerListFilter
组件来搭配使用。
到目前为止,Ribbon的五大组件都简单的介绍了一遍IPing
、IRule
、ServerList
、ServerListFilter
、ServerListUpdater
。Ribbon的LoadBalance就是对这些基础组件的组合协调。