最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

解讀Eureka的TimedSupervisorTask類(自動調(diào)節(jié)間隔的周期性任務(wù))

 更新時間:2024年11月01日 08:58:55   作者:程序員欣宸  
在Eureka客戶端中,盡管ScheduledExecutorService的schedule方法創(chuàng)建的是一次性任務(wù),但通過在任務(wù)執(zhí)行完畢后再次調(diào)用schedule方法實現(xiàn)了周期性執(zhí)行,這種設(shè)計既考慮到了任務(wù)超時導(dǎo)致的間隔時間調(diào)整,又通過CAS實現(xiàn)了多線程同步,展現(xiàn)了簡潔而巧妙的設(shè)計思想

起因

一個基于Spring Cloud框架的應(yīng)用,如果注冊到了Eureka server,那么它就會定時更新服務(wù)列表,

這個定時任務(wù)啟動的代碼在com.netflix.discovery.DiscoveryClient類的initScheduledTasks方法中,

如下(來自工程eureka-client,版本1.7.0):

private void initScheduledTasks() {
		//更新服務(wù)列表
        if (clientConfig.shouldFetchRegistry()) {
            // registry cache refresh timer
            int registryFetchIntervalSeconds = clientConfig.getRegistryFetchIntervalSeconds();
            int expBackOffBound = clientConfig.getCacheRefreshExecutorExponentialBackOffBound();
            scheduler.schedule(
                    new TimedSupervisorTask(
                            "cacheRefresh",
                            scheduler,
                            cacheRefreshExecutor,
                            registryFetchIntervalSeconds,
                            TimeUnit.SECONDS,
                            expBackOffBound,
                            new CacheRefreshThread()
                    ),
                    registryFetchIntervalSeconds, TimeUnit.SECONDS);
        }
		...
		//略去其他代碼

上述代碼中,scheduler是ScheduledExecutorService接口的實現(xiàn),其schedule方法的官方文檔如下所示:

上圖紅框顯示:

該方法創(chuàng)建的是一次性任務(wù),但是在實際測試中,如果在CacheRefreshThread類的run方法中打個斷點,就會發(fā)現(xiàn)該方法會被周期性調(diào)用;

因此問題就來了:

方法schedule(Callable callable,long delay,TimeUnit unit)創(chuàng)建的明明是個一次性任務(wù),但CacheRefreshThread被周期性執(zhí)行了;

尋找答案

打開的run方法源碼,請注意下面的中文注釋:

public void run() {
        Future future = null;
        try {
	    //使用Future,可以設(shè)定子線程的超時時間,這樣當(dāng)前線程就不用無限等待了
            future = executor.submit(task);
            threadPoolLevelGauge.set((long) executor.getActiveCount());
            //指定等待子線程的最長時間
            future.get(timeoutMillis, TimeUnit.MILLISECONDS);  // block until done or timeout
            //delay是個很有用的變量,后面會用到,這里記得每次執(zhí)行任務(wù)成功都會將delay重置
            delay.set(timeoutMillis);
            threadPoolLevelGauge.set((long) executor.getActiveCount());
        } catch (TimeoutException e) {
            logger.error("task supervisor timed out", e);
            timeoutCounter.increment();

            long currentDelay = delay.get();
            //任務(wù)線程超時的時候,就把delay變量翻倍,但不會超過外部調(diào)用時設(shè)定的最大延時時間
            long newDelay = Math.min(maxDelay, currentDelay * 2);
            //設(shè)置為最新的值,考慮到多線程,所以用了CAS
            delay.compareAndSet(currentDelay, newDelay);
        } catch (RejectedExecutionException e) {
            //一旦線程池的阻塞隊列中放滿了待處理任務(wù),觸發(fā)了拒絕策略,就會將調(diào)度器停掉
            if (executor.isShutdown() || scheduler.isShutdown()) {
                logger.warn("task supervisor shutting down, reject the task", e);
            } else {
                logger.error("task supervisor rejected the task", e);
            }

            rejectedCounter.increment();
        } catch (Throwable e) {
            //一旦出現(xiàn)未知的異常,就停掉調(diào)度器
            if (executor.isShutdown() || scheduler.isShutdown()) {
                logger.warn("task supervisor shutting down, can't accept the task");
            } else {
                logger.error("task supervisor threw an exception", e);
            }

            throwableCounter.increment();
        } finally {
            //這里任務(wù)要么執(zhí)行完畢,要么發(fā)生異常,都用cancel方法來清理任務(wù);
            if (future != null) {
                future.cancel(true);
            }
            
	    //只要調(diào)度器沒有停止,就再指定等待時間之后在執(zhí)行一次同樣的任務(wù)
            if (!scheduler.isShutdown()) {
            	//這里就是周期性任務(wù)的原因:只要沒有停止調(diào)度器,就再創(chuàng)建一次性任務(wù),執(zhí)行時間時dealy的值,
            	//假設(shè)外部調(diào)用時傳入的超時時間為30秒(構(gòu)造方法的入?yún)imeout),最大間隔時間為50秒(構(gòu)造方法的入?yún)xpBackOffBound)
            	//如果最近一次任務(wù)沒有超時,那么就在30秒后開始新任務(wù),
            	//如果最近一次任務(wù)超時了,那么就在50秒后開始新任務(wù)(異常處理中有個乘以二的操作,乘以二后的60秒超過了最大間隔50秒)
                scheduler.schedule(this, delay.get(), TimeUnit.MILLISECONDS);
            }
        }
    }

真相就在上面的最后一行代碼中:

scheduler.schedule(this, delay.get(), TimeUnit.MILLISECONDS)

執(zhí)行完任務(wù)后,會再次調(diào)用schedule方法,在指定的時間之后執(zhí)行一次相同的任務(wù),這個間隔時間和最近一次任務(wù)是否超時有關(guān),如果超時了就間隔時間就會變大;

總結(jié)

從整體上看,TimedSupervisorTask是固定間隔的周期性任務(wù),一旦遇到超時就會將下一個周期的間隔時間調(diào)大,如果連續(xù)超時,那么每次間隔時間都會增大一倍,一直到達外部參數(shù)設(shè)定的上限為止,一旦新任務(wù)不再超時,間隔時間又會自動恢復(fù)為初始值,另外還有CAS來控制多線程同步,簡潔的代碼,巧妙的設(shè)計,值得我們學(xué)習(xí);

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • idea中@override報錯在實現(xiàn)接口方法時無法使用的解決

    idea中@override報錯在實現(xiàn)接口方法時無法使用的解決

    文章介紹了在使用IDEA新建Filter類時遇到的@Override注解報錯問題,原因是JDK版本過低,通過在maven配置文件中設(shè)置編譯插件或更改項目JDK版本,解決了該問題
    2025-12-12
  • java中判斷字段真實長度的實例(中文2個字符,英文1個字符)

    java中判斷字段真實長度的實例(中文2個字符,英文1個字符)

    下面小編就為大家?guī)硪黄猨ava中判斷字段真實長度的實例(中文2個字符,英文1個字符)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-01-01
  • java Arrays快速打印數(shù)組的數(shù)據(jù)元素列表案例

    java Arrays快速打印數(shù)組的數(shù)據(jù)元素列表案例

    這篇文章主要介紹了java Arrays快速打印數(shù)組的數(shù)據(jù)元素列表案例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-09-09
  • Java接口自動化測試框架設(shè)計之Get請求方法和測試詳解

    Java接口自動化測試框架設(shè)計之Get請求方法和測試詳解

    這篇文章主要介紹了Java接口自動化測試框架設(shè)計 Get請求方法和測試,框架設(shè)計我們只是介紹基本的組件,而且框架設(shè)計沒有想象那么難,一步一步跟著做就會了。這篇我們來演示,如果通過Java代碼來實現(xiàn)一個用純代碼實現(xiàn)Http中的Get請求過程,需要的朋友可以參考下
    2019-07-07
  • Maven build 命令介紹的使用詳解

    Maven build 命令介紹的使用詳解

    這篇文章主要介紹了Maven build 命令介紹的使用詳解,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-06-06
  • 基于Spring AI+MySQL實現(xiàn)文件內(nèi)容相似度的簡單檢測

    基于Spring AI+MySQL實現(xiàn)文件內(nèi)容相似度的簡單檢測

    在做一個 AI 求職的比賽項目中,遇到了一個有趣的需求:用戶上傳自己的簡歷文件后,系統(tǒng)需要計算并返回該簡歷與其他用戶簡歷之間的相似度,想要實現(xiàn)這樣的功能,所以本文給大家分享了如何基于Spring AI+MySQL實現(xiàn)文件內(nèi)容相似度的簡單檢測,需要的朋友可以參考下
    2025-11-11
  • Java線程的基本概念

    Java線程的基本概念

    本文主要介紹了Java線程的基本概念。具有很好的參考價值,下面跟著小編一起來看下吧
    2017-02-02
  • JVM運行時數(shù)據(jù)區(qū)劃分原理詳解

    JVM運行時數(shù)據(jù)區(qū)劃分原理詳解

    這篇文章主要介紹了JVM運行時數(shù)據(jù)區(qū)劃分原理詳解,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-05-05
  • 深入理解Java中1是true0是false

    深入理解Java中1是true0是false

    Java中,1可以被看作是true,0可以被看作是false,本文就來進行詳細的講解,具有一定的參考價值,感興趣的可以了解一下
    2024-02-02
  • Java中內(nèi)存區(qū)域的劃分與異常詳解

    Java中內(nèi)存區(qū)域的劃分與異常詳解

    最近在看java虛擬相關(guān)知識,把每天看到的一些內(nèi)容做一個歸納總結(jié),下面這篇文章主要給大家介紹了關(guān)于Java中內(nèi)存區(qū)域的劃分與異常的相關(guān)資料,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考借鑒,下面來一起看看吧
    2018-06-06

最新評論

英超| 尚义县| 青河县| 鄂托克前旗| 砀山县| 大田县| 东明县| 吴江市| 南京市| 酒泉市| 大庆市| 永胜县| 福安市| 通州市| 西充县| 西畴县| 静安区| 广昌县| 寻乌县| 贵德县| 景洪市| 尚志市| 颍上县| 新安县| 永善县| 华亭县| 建湖县| 新沂市| 永靖县| 九龙县| 兴义市| 贡山| 资阳市| 腾冲县| 东安县| 修武县| 盐亭县| 从化市| 池州市| 甘孜| 疏附县|