ribbon中,发现一段比较有水平的代码.md

ribbon中,发现一段比较有水平的代码#

简介#

有个类,com.netflix.loadbalancer.PollingServerListUpdater,主要功能是,获取服务的instance列表。

这也是ribbon中的核心代码。

Copy
public class PollingServerListUpdater implements ServerListUpdater {

该接口的功能,给大家看下:

Copy
public interface ServerListUpdater { /** * 1 an interface for the updateAction that actually executes a server list update */ public interface UpdateAction { void doUpdate(); } /** * start the serverList updater with the given update action * This call should be idempotent. * * @param updateAction */ void start(UpdateAction updateAction); /** * stop the serverList updater. This call should be idempotent */ void stop(); /** * @return the last update timestamp as a {@link java.util.Date} string */ String getLastUpdate(); /** * @return the number of ms that has elapsed since last update */ long getDurationSinceLastUpdateMs(); /** * @return the number of update cycles missed, if valid */ int getNumberMissedCycles(); /** * @return the number of threads used, if vaid */ int getCoreThreads(); }

其实这个接口的几个方法,主要就是,维护了一个线程池,然后定期去调用1处的UpdateAction,获取最新的服务列表,比如调用eureka client的方法,去eureka server获取服务列表。

静态类单例模式#

下面这个方法,获取线程池,里面用了一个静态内部类:

Copy
private static ScheduledThreadPoolExecutor getRefreshExecutor() { return LazyHolder._serverListRefreshExecutor; }
Copy
com.netflix.loadbalancer.PollingServerListUpdater.LazyHolder 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 { // 1 int coreSize = poolSizeProp.get(); ThreadFactory factory = (new ThreadFactoryBuilder()) .setNameFormat("PollingServerListUpdater-%d") .setDaemon(true) .build(); // 2 _serverListRefreshExecutor = new ScheduledThreadPoolExecutor(coreSize, factory); // 3 poolSizeProp.addCallback(new Runnable() { @Override public void run() { // 4 _serverListRefreshExecutor.setCorePoolSize(poolSizeProp.get()); } }); // 5 _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) { // NOPMD // this can happen if we're in the middle of a real // shutdown, // and that's 'ok' } } } } }
  • 1处,获取要构造的线程池的core线程的数量

  • 2处,new了一个线程池,使用的就是jdk里面的java.util.concurrent.ScheduledThreadPoolExecutor

  • 3处,这里的poolSizeProp,可以通过Archaius来进行修改,这个Archaius听说挺强的,但是没用过,可以理解为一个配置中心吧,可以通过Archaius修改线程池的线程数量,这里加了个回调,意思是,如果线程池数量变更了,调用这个回调方法

  • 4处,这里直接调用了ScheduledThreadPoolExecutor的setCorePoolSize方法,大家可以看看下面的代码:

    Copy
    java.util.concurrent.ThreadPoolExecutor#setCorePoolSize public void setCorePoolSize(int corePoolSize) { if (corePoolSize < 0) throw new IllegalArgumentException(); int delta = corePoolSize - this.corePoolSize; // 4.1 this.corePoolSize = corePoolSize; if (workerCountOf(ctl.get()) > corePoolSize) interruptIdleWorkers(); else if (delta > 0) { // We don't really know how many new threads are "needed". // As a heuristic, prestart enough new workers (up to new // core size) to handle the current number of tasks in // queue, but stop if queue becomes empty while doing so. int k = Math.min(delta, workQueue.size()); while (k-- > 0 && addWorker(null, true)) { if (workQueue.isEmpty()) break; } } }

    看看,4.1,这里对field:corePoolSize进行了修改。

    Copy
    private volatile int corePoolSize;

    那,大家明白为啥要定义成volatile了吗?

  • 5处,添加一个ShutdownHook,进程关闭时(kill 9不行,要kill 15),会回调该ShutdownHook,关闭线程池。

线程池开始获取服务列表#

Copy
private final AtomicBoolean isActive = new AtomicBoolean(false); // 1 private volatile long lastUpdated = System.currentTimeMillis(); private final long initialDelayMs; private final long refreshIntervalMs; private volatile ScheduledFuture<?> scheduledFuture; @Override public synchronized void start(final UpdateAction updateAction) { // 2 if (isActive.compareAndSet(false, true)) { final Runnable wrapperRunnable = new Runnable() { @Override public void run() { // 3 if (!isActive.get()) { if (scheduledFuture != null) { // 4 scheduledFuture.cancel(true); } return; } try { // 5 updateAction.doUpdate(); lastUpdated = System.currentTimeMillis(); } catch (Exception e) { logger.warn("Failed one update cycle", e); } } }; // 6 scheduledFuture = getRefreshExecutor().scheduleWithFixedDelay( wrapperRunnable, initialDelayMs, refreshIntervalMs, TimeUnit.MILLISECONDS ); } else { logger.info("Already active, no-op"); } }
  • 1处,需要跨线程访问的,该加volatile就加
  • 2处,使用原子类,避免多次被执行
  • 3处,使用原子boolean作为开关,方便线程及时停止运行
  • 4处,停止运行时,不忘记关闭定时任务,秀。
  • 5处,可以算作是策略或者模板方法模式吧
  • 6处,新建线程池,重点是保存返回的future

线程池如何停止#

Copy
@Override public synchronized void stop() { if (isActive.compareAndSet(true, false)) { if (scheduledFuture != null) { scheduledFuture.cancel(true); } } else { logger.info("Not active, no-op"); } }

其他几个方法实现#

Copy
@Override public String getLastUpdate() { return new Date(lastUpdated).toString(); } @Override public long getDurationSinceLastUpdateMs() { return System.currentTimeMillis() - lastUpdated; } @Override public int getNumberMissedCycles() { if (!isActive.get()) { return 0; } return (int) ((int) (System.currentTimeMillis() - lastUpdated) / refreshIntervalMs); } @Override public int getCoreThreads() { if (isActive.get()) { if (getRefreshExecutor() != null) { return getRefreshExecutor().getCorePoolSize(); } } return 0; } private static long getRefreshIntervalMs(IClientConfig clientConfig) { return clientConfig.get(CommonClientConfigKey.ServerListRefreshInterval, LISTOFSERVERS_CACHE_REPEAT_INTERVAL); }



posted @   三国梦回  阅读(951)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
历史上的今天:
2018-06-29 JVM工具jinfo实践
2018-06-29 【JVM译文】JVM问题定位前的准备工作有哪些
点击右上角即可分享
微信分享提示
CONTENTS