欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

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

起因

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

這個(gè)定時(shí)任務(wù)啟動(dòng)的代碼在com.netflix.discovery.DiscoveryClient類的initScheduledTasks方法中,

如下(來(lái)自工程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接口的實(shí)現(xiàn),其schedule方法的官方文檔如下所示:

上圖紅框顯示:

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

因此問(wèn)題就來(lái)了:

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

尋找答案

打開的run方法源碼,請(qǐng)注意下面的中文注釋:

public void run() {
        Future future = null;
        try {
	    //使用Future,可以設(shè)定子線程的超時(shí)時(shí)間,這樣當(dāng)前線程就不用無(wú)限等待了
            future = executor.submit(task);
            threadPoolLevelGauge.set((long) executor.getActiveCount());
            //指定等待子線程的最長(zhǎng)時(shí)間
            future.get(timeoutMillis, TimeUnit.MILLISECONDS);  // block until done or timeout
            //delay是個(gè)很有用的變量,后面會(huì)用到,這里記得每次執(zhí)行任務(wù)成功都會(huì)將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ù)線程超時(shí)的時(shí)候,就把delay變量翻倍,但不會(huì)超過(guò)外部調(diào)用時(shí)設(shè)定的最大延時(shí)時(shí)間
            long newDelay = Math.min(maxDelay, currentDelay * 2);
            //設(shè)置為最新的值,考慮到多線程,所以用了CAS
            delay.compareAndSet(currentDelay, newDelay);
        } catch (RejectedExecutionException e) {
            //一旦線程池的阻塞隊(duì)列中放滿了待處理任務(wù),觸發(fā)了拒絕策略,就會(huì)將調(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方法來(lái)清理任務(wù);
            if (future != null) {
                future.cancel(true);
            }
            
	    //只要調(diào)度器沒(méi)有停止,就再指定等待時(shí)間之后在執(zhí)行一次同樣的任務(wù)
            if (!scheduler.isShutdown()) {
            	//這里就是周期性任務(wù)的原因:只要沒(méi)有停止調(diào)度器,就再創(chuàng)建一次性任務(wù),執(zhí)行時(shí)間時(shí)dealy的值,
            	//假設(shè)外部調(diào)用時(shí)傳入的超時(shí)時(shí)間為30秒(構(gòu)造方法的入?yún)imeout),最大間隔時(shí)間為50秒(構(gòu)造方法的入?yún)xpBackOffBound)
            	//如果最近一次任務(wù)沒(méi)有超時(shí),那么就在30秒后開始新任務(wù),
            	//如果最近一次任務(wù)超時(shí)了,那么就在50秒后開始新任務(wù)(異常處理中有個(gè)乘以二的操作,乘以二后的60秒超過(guò)了最大間隔50秒)
                scheduler.schedule(this, delay.get(), TimeUnit.MILLISECONDS);
            }
        }
    }

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

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

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

總結(jié)

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

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

相關(guān)文章

  • Mybatis-plus多數(shù)據(jù)源配置的兩種方式總結(jié)

    Mybatis-plus多數(shù)據(jù)源配置的兩種方式總結(jié)

    這篇文章主要為大家詳細(xì)介紹了Mybatis-plus中多數(shù)據(jù)源配置的兩種方式,文中的示例代碼簡(jiǎn)潔易懂,感興趣的小伙伴可以跟隨小編一起了解一下
    2022-10-10
  • 基于Spring中各個(gè)jar包的作用及依賴(詳解)

    基于Spring中各個(gè)jar包的作用及依賴(詳解)

    下面小編就為大家?guī)?lái)一篇基于Spring中各個(gè)jar包的作用及依賴(詳解)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-11-11
  • Java?方法的重載與參數(shù)傳遞詳解

    Java?方法的重載與參數(shù)傳遞詳解

    在java中,方法就是用來(lái)完成解決某件事情或?qū)崿F(xiàn)某個(gè)功能的辦法。方法實(shí)現(xiàn)的過(guò)程中,會(huì)包含很多條語(yǔ)句用于完成某些有意義的功能——通常是處理文本,控制輸入或計(jì)算數(shù)值,這篇文章我們來(lái)探究一下方法的重載與傳參
    2022-04-04
  • mybatis的mapper特殊字符轉(zhuǎn)移及動(dòng)態(tài)SQL條件查詢小結(jié)

    mybatis的mapper特殊字符轉(zhuǎn)移及動(dòng)態(tài)SQL條件查詢小結(jié)

    mybatis mapper文件中條件查詢符,如>=,<,之類是不能直接寫的會(huì)報(bào)錯(cuò)的需要轉(zhuǎn)移一下,本文給大家介紹了常見的條件查詢操作,對(duì)mybatis的mapper特殊字符及動(dòng)態(tài)SQL條件查詢相關(guān)知識(shí)感興趣的朋友一起看看吧
    2021-09-09
  • 解決Eclipse發(fā)布到Tomcat丟失依賴jar包的問(wèn)題

    解決Eclipse發(fā)布到Tomcat丟失依賴jar包的問(wèn)題

    這篇文章介紹了如何在Eclipse中配置部署裝配功能,以確保在將Web項(xiàng)目發(fā)布到Tomcat服務(wù)器時(shí)不會(huì)丟失任何依賴jar包,通過(guò)手動(dòng)配置或使用構(gòu)建工具腳本,可以自動(dòng)化這個(gè)過(guò)程,提高開發(fā)效率和應(yīng)用程序的穩(wěn)定性,感興趣的朋友跟隨小編一起看看吧
    2025-01-01
  • SpringBoot Maven打包失敗報(bào):class lombok.javac.apt.LombokProcessor錯(cuò)誤的解決辦法

    SpringBoot Maven打包失敗報(bào):class lombok.javac.apt.Lombo

    最新項(xiàng)目部署的時(shí)候,出現(xiàn)了一個(gè)maven打包失敗的問(wèn)題,報(bào):class lombok.javac.apt.LombokProcessor錯(cuò)誤,所以本文給大家介紹了如何解決SpringBoot Maven 打包失?。篶lass lombok.javac.apt.LombokProcessor 錯(cuò)誤,需要的朋友可以參考下
    2023-12-12
  • JAVA之反射機(jī)制Reflection的使用

    JAVA之反射機(jī)制Reflection的使用

    這篇文章主要介紹了JAVA之反射機(jī)制Reflection的使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2025-04-04
  • 三分鐘帶你掌握J(rèn)ava開發(fā)圖片驗(yàn)證碼功能方法

    三分鐘帶你掌握J(rèn)ava開發(fā)圖片驗(yàn)證碼功能方法

    這篇文章主要來(lái)為大家詳細(xì)介紹Java實(shí)現(xiàn)開發(fā)圖片驗(yàn)證碼的具體方法,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,需要的可以參考一下
    2023-02-02
  • JAVA數(shù)組練習(xí)題實(shí)例講解

    JAVA數(shù)組練習(xí)題實(shí)例講解

    這篇文章主要給大家介紹了關(guān)于JAVA數(shù)組練習(xí)題的相關(guān)資料,這是個(gè)人總結(jié)的一些關(guān)于java數(shù)組的練習(xí)題,文中通過(guò)代碼實(shí)例介紹的非常詳細(xì),需要的朋友可以參考下
    2023-08-08
  • spring?cloud之eureka高可用集群和服務(wù)分區(qū)解析

    spring?cloud之eureka高可用集群和服務(wù)分區(qū)解析

    這篇文章主要介紹了spring?cloud之eureka高可用集群和服務(wù)分區(qū)解析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03

最新評(píng)論