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

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

简介

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

这也是ribbon中的核心代码。

public class PollingServerListUpdater implements ServerListUpdater {

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

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获取服务列表。

静态类单例模式

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

    private static ScheduledThreadPoolExecutor getRefreshExecutor() {
        return LazyHolder._serverListRefreshExecutor;
    }
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方法,大家可以看看下面的代码:

       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进行了修改。

    private volatile int corePoolSize;
    

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

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

线程池开始获取服务列表

	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

线程池如何停止

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

其他几个方法实现


    @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 @ 2020-06-29 20:29  三国梦回  阅读(940)  评论(0编辑  收藏  举报