图解+源码讲解 Ribbon 服务列表更新

1,266 阅读4分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 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 该策略】如下图
image.png

@Bean
@ConditionalOnMissingBean
public ServerListUpdater ribbonServerListUpdater(IClientConfig config) {
    return new PollingServerListUpdater(config);
}

image.png

定时拉取更新的服务列表 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() 这个方法和上面的后续一样

小结

  1. 两种方式进行服务列表更新
  2. 一种是定时拉取更新列表操作
  3. 一种是 eureka 缓存刷新通知事件进行服务列表更新