一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第13天,点击查看活动详情
图解+源码讲解 Ribbon 服务列表更新
构成天才的决定因素就应是勤奋 —— 郭沫若 ribbon 相关文章
图解+源码讲解 Ribbon 如何获取注册中心的实例
图解+源码讲解 Ribbon 原理初探
图解+源码讲解 Ribbon 服务列表更新
图解+源码讲解 Ribbon 服务选择原理
Ribbon 原理初探
eureka 相关文章
eureka-server 项目结构分析
图解+源码讲解 Eureka Server 启动流程分析
图解+源码讲解 Eureka Client 启动流程分析
图解+源码讲解 Eureka Server 注册表缓存逻辑
图解+源码讲解 Eureka Client 拉取注册表流程
图解+源码讲解 Eureka Client 服务注册流程
图解+源码讲解 Eureka Client 心跳机制流程
图解+源码讲解 Eureka Client 下线流程分析
图解+源码讲解 Eureka Server 服务剔除逻辑
图解+源码讲解 Eureka Server 集群注册表同步机制
动态服务负载均衡器 DynamicServerListLoadBalancer
在这个负载均衡器里面有一个 restOfInit 这个方法,这个方法涉及到注册中心的服务列表的拉取,以及拉取或者监听更新列表的变化,之后设置到负载均衡器里面,提供给请求执行的时候,进行服务选择
public DynamicServerListLoadBalancer(IClientConfig clientConfig, IRule rule,
IPing ping, ServerList<T> serverList, ServerListFilter<T> filter,
ServerListUpdater serverListUpdater) {
super(clientConfig, rule, ping);
this.serverListImpl = serverList;
this.filter = filter;
this.serverListUpdater = serverListUpdater;
if (filter instanceof AbstractServerListFilter) {
((AbstractServerListFilter) filter).
setLoadBalancerStats(getLoadBalancerStats());
}
// 核心方法,其他的方法先不细看
restOfInit(clientConfig);
}
这个方法里面 enableAndInitLearnNewServersFeature 这个是一个核心的方法,用来进行指定拉取或者监听服务变化列表
void restOfInit(IClientConfig clientConfig) {
boolean primeConnection = this.isEnablePrimingConnections();
// turn this off to avoid duplicated asynchronous priming done in BaseLoadBalancer.setServerList()
this.setEnablePrimingConnections(false);
enableAndInitLearnNewServersFeature();
updateListOfServers();
if (primeConnection && this.getPrimeConnections() != null) {
this.getPrimeConnections()
.primeConnections(getReachableServers());
}
this.setEnablePrimingConnections(primeConnection);
LOGGER.info("DynamicServerListLoadBalancer for client {} initialized: {}", clientConfig.getClientName(), this.toString());
}
服务列表变更操作 enableAndInitLearnNewServersFeature
开启服务列表变化更新通知操作
public void enableAndInitLearnNewServersFeature() {
// 服务列表更新器开启 updateAction 更新操作
serverListUpdater.start(updateAction);
}
protected final ServerListUpdater.UpdateAction updateAction =
new ServerListUpdater.UpdateAction() {
@Override
public void doUpdate() {
// 这个就是上面讲解的服务实例拉取操作
updateListOfServers();
}
};
服务列表变更策略
定时拉取策略
serverListUpdater.start 方法有两种实现策略,一个是 eureka 服务通知操作,一个是主动的去拉取服务变更操作,默认的是 PollingServerListUpdater 更新服务策略【在 RibbonClientConfiguration 里面已经进行创建了PollingServerListUpdater 该策略】如下图
@Bean
@ConditionalOnMissingBean
public ServerListUpdater ribbonServerListUpdater(IClientConfig config) {
return new PollingServerListUpdater(config);
}
定时拉取更新的服务列表 PollingServerListUpdater
30s 去拉取一次服务列表,看服务列表是否有变化
private static long LISTOFSERVERS_CACHE_UPDATE_DELAY = 1000; // msecs;
// 30s 去拉取一次
private static int LISTOFSERVERS_CACHE_REPEAT_INTERVAL = 30 * 1000; // msecs;
public PollingServerListUpdater() {
this(LISTOFSERVERS_CACHE_UPDATE_DELAY, LISTOFSERVERS_CACHE_REPEAT_INTERVAL);
}
public PollingServerListUpdater(final long initialDelayMs, final long refreshIntervalMs) {
this.initialDelayMs = initialDelayMs;
this.refreshIntervalMs = refreshIntervalMs;
}
PollingServerListUpdater#start 方法
创建了一个线程调度器 scheduledFuture 去按照固定的延时和频率去执行 wrapperRunnable 任务
@Override
public synchronized void start(final UpdateAction updateAction) {
// 创建一个线程
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);
}
}
};
// 线程调度器调度 wrapperRunnable 任务
scheduledFuture = getRefreshExecutor().scheduleWithFixedDelay(
wrapperRunnable,
initialDelayMs,
refreshIntervalMs,
TimeUnit.MILLISECONDS
);
}
updateAction.doUpdate() 定时更新服务列表
updateAction 是之前 enableAndInitLearnNewServersFeature() 这个方法里面传进来的 UpdateAction,其实就是定时去调用 updateListOfServers() 方法去更新服务列表
protected final ServerListUpdater.UpdateAction updateAction = new ServerListUpdater.UpdateAction() {
@Override
public void doUpdate() {
updateListOfServers();
}
};
服务监听策略
Eureka通知服务列表更新器 EurekaNotificationServerListUpdater
// eureka事件监听器
private volatile EurekaEventListener updateListener;
// eureka 客户端
private volatile EurekaClient eurekaClient;
public EurekaNotificationServerListUpdater() {
this(new LegacyEurekaClientProvider());
}
public EurekaNotificationServerListUpdater(final Provider<EurekaClient>
eurekaClientProvider) {
this(eurekaClientProvider, getDefaultRefreshExecutor());
}
// 构造 eureka通知服务列表监听器
public EurekaNotificationServerListUpdater(final Provider<EurekaClient>
eurekaClientProvider, ExecutorService refreshExecutor) {
this.eurekaClientProvider = eurekaClientProvider;
this.refreshExecutor = refreshExecutor;
}
EurekaNotificationServerListUpdater#start 方法
public synchronized void start(final UpdateAction updateAction) {
if (isActive.compareAndSet(false, true)) {
// 创建了一个eureka事件监听器
this.updateListener = new EurekaEventListener() {
@Override
public void onEvent(EurekaEvent event) {
// 缓存刷新事件
if (event instanceof CacheRefreshedEvent) {
if (!refreshExecutor.isShutdown()) {
refreshExecutor.submit(new Runnable() {
@Override
public void run() {
try {
// 进行服务列表更新
updateAction.doUpdate();
lastUpdated.set(System.currentTimeMillis());
} catch (Exception e) {
logger.warn("Failed to update serverList", e);
} finally {
updateQueued.set(false);
}
}
});
}
else {
stop();
}
}
}
};
if (eurekaClient == null) {
// 获取 eureka 客户端
eurekaClient = eurekaClientProvider.get();
}
if (eurekaClient != null) {
// 注册监听器
eurekaClient.registerEventListener(updateListener);
}
}
updateAction.doUpdate() 这个方法和上面的后续一样
小结
- 两种方式进行服务列表更新
- 一种是定时拉取更新列表操作
- 一种是 eureka 缓存刷新通知事件进行服务列表更新