溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

大數(shù)據(jù)開發(fā)中定時(shí)器有哪些

發(fā)布時(shí)間:2022-01-13 09:31:46 來源:億速云 閱讀:192 作者:小新 欄目:大數(shù)據(jù)

這篇文章給大家分享的是有關(guān)大數(shù)據(jù)開發(fā)中定時(shí)器有哪些的內(nèi)容。小編覺得挺實(shí)用的,因此分享給大家做個(gè)參考,一起跟隨小編過來看看吧。

前言

何為定時(shí)器,說白了就是指定一個(gè)延遲時(shí)間,到期執(zhí)行,就像我們早上定的鬧鈴一樣,每天定點(diǎn)提醒我們起床;當(dāng)然在我們各個(gè)系統(tǒng)中也是無處不在,比如定時(shí)備份數(shù)據(jù),定時(shí)拉取文件,定時(shí)刷新數(shù)據(jù)等等;定時(shí)器工具也是層出不窮比如Timer,ScheduledExecutorService,Spring Scheduler,HashedWheelTimer(時(shí)間輪),Quartz,Xxl-job/Elastic-job等;本文將對這些定時(shí)器工具做個(gè)簡單介紹和對比,都在哪些場景下使用。

Timer

Timer可以說是JDK提供最早的一個(gè)定時(shí)器了,使用簡單,功能也相對來說比較簡單;可以指定固定時(shí)間后觸發(fā),固定時(shí)間點(diǎn)觸發(fā),固定頻率觸發(fā);

Timer timer = new Timer();
timer.schedule(new TimerTask() {@Overridepublic void run() {
        System.out.println(System.currentTimeMillis() + "  === task1");
    }
}, 1000, 1000);

時(shí)間默認(rèn)為毫秒,表示延遲一秒后執(zhí)行任務(wù),并且頻率為1秒執(zhí)行任務(wù);Timer內(nèi)部使用TaskQueue存放任務(wù),使用TimerThread單線程用來執(zhí)行任務(wù):

private final TaskQueue queue = new TaskQueue();private final TimerThread thread = new TimerThread(queue);

TimerThread內(nèi)部是一個(gè)while(true)循環(huán),不停的從TaskQueue中獲取任務(wù)執(zhí)行;當(dāng)然每次添加到TaskQueue中的任務(wù)會進(jìn)行排序,通過nextExecutionTime來進(jìn)行排序,這樣TimerThread每次都可以獲取到最近執(zhí)行的任務(wù);
Timer有兩大缺點(diǎn):

  • TimerTask中出現(xiàn)未捕獲的異常,影響Timer;

  • 因?yàn)槭菃尉€程執(zhí)行某個(gè)任務(wù)執(zhí)行時(shí)間過長會影響其他認(rèn)為的精確度;

正因?yàn)門imer存在的一些缺點(diǎn),JDK1.5出現(xiàn)了新的定時(shí)器ScheduledExecutorService;

ScheduledExecutorService

JDK1.5提供了線程池的功能,ScheduledExecutorService是一個(gè)接口類,具體實(shí)現(xiàn)類是ScheduledThreadPoolExecutor繼承于ThreadPoolExecutor;

ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);
scheduledExecutorService.scheduleAtFixedRate(new Runnable() {@Overridepublic void run() {
        System.out.println(Thread.currentThread() + " === " + System.currentTimeMillis() + " === task1");
    }
}, 1000, 1000, TimeUnit.MILLISECONDS);

比起Timer可以配置多個(gè)線程去執(zhí)行定時(shí)任務(wù),同時(shí)異常任務(wù)并不會中斷ScheduledExecutorService,線程池的幾個(gè)核心配置:

  • corePoolSize:核心線程數(shù)量,如果線程池中的線程少于此數(shù)目,則在執(zhí)行任務(wù)時(shí)創(chuàng)建,核心線程不會被回收;

  • maximumPoolSize:允許的最大線程數(shù),當(dāng)線程數(shù)量達(dá)到corePoolSize且workQueue隊(duì)列滿了,會創(chuàng)建線程;

  • keepAliveTime:超過corePoolSize空閑時(shí)間;

  • unit:keepAliveTime的單位;

  • workQueue:當(dāng)線程超過corePoolSize,新的任務(wù)會被加入到隊(duì)列中等待;

  • threadFactory:創(chuàng)建線程的工廠類;

  • handler:線程池拒絕策略,包括:AbortPolicy,DiscardPolicy,DiscardOldestPolicy,CallerRunsPolicy策略,當(dāng)然也可以自己擴(kuò)展;

ScheduledExecutorService中添加的任務(wù)會被包裝成一個(gè)ScheduledFutureTask類,同時(shí)將任務(wù)放入DelayedWorkQueue隊(duì)列中是一個(gè)BlockingQueue;類似Timer也會根據(jù)加入任務(wù)觸發(fā)時(shí)間的先后進(jìn)行排序,然后線程池中的Worker會到Queue中獲取任務(wù)執(zhí)行;

Spring Scheduler

Spring提供了xml和注解方式來配置調(diào)度任務(wù),如下面xml配置:

<!-- 創(chuàng)建一個(gè)調(diào)度器 --><task:scheduler id="scheduler" /><!-- 配置任務(wù)類的bean --><bean id="helloTask" class="com.spring.task.HelloTask"></bean><task:scheduled-tasks scheduler="scheduler"><!-- 每2秒執(zhí)行一次 --><task:scheduled ref="helloTask" method="say" cron="0/2 * * * * ?" /></task:scheduled-tasks>

Spring提供了cron表達(dá)式的支持,并且可以直接配置執(zhí)行指定類中的指定方法,對使用者來說更加方便和簡單;但是其內(nèi)部還是使用的ScheduledThreadPoolExecutor線程池;

HashedWheelTimer

Netty提供的一個(gè)定時(shí)器,用于定時(shí)發(fā)送心跳,使用的是時(shí)間輪算法;HashedWheelTimer是一個(gè)環(huán)形結(jié)構(gòu),可以類比成一個(gè)時(shí)鐘,整個(gè)環(huán)形結(jié)構(gòu)由一個(gè)個(gè)小格組成,每個(gè)小格可以存放很多任務(wù),隨著時(shí)間的流逝,指針轉(zhuǎn)動,然后執(zhí)行當(dāng)前指定格子中的任務(wù);任務(wù)通過取模的方式?jīng)Q定其應(yīng)該放在哪個(gè)格子,有點(diǎn)類似hashmap;

HashedWheelTimer hashedWheelTimer = new HashedWheelTimer(1000, TimeUnit.MILLISECONDS, 16);
hashedWheelTimer.newTimeout(new TimerTask() {@Overridepublic void run(Timeout timeout) throws Exception {
        System.out.println(System.currentTimeMillis() + "  === executed");
    }
}, 1, TimeUnit.SECONDS);

其中初始化的三個(gè)參數(shù)分別是:

  • tickDuration:每一格的時(shí)長;

  • unit:tickDuration的單位;

  • ticksPerWheel:時(shí)間輪總共有多少格;

如上面實(shí)例配置的參數(shù),每一格時(shí)長1秒,時(shí)間輪總共16格,如果延遲1秒執(zhí)行,那就放到編號1的格子中,從0開始;如果延遲18秒,那么會放到編號為2的格子中,同時(shí)指定remainingRounds=1,表示第幾輪被調(diào)用,每轉(zhuǎn)一輪remainingRounds-1,知道remainingRounds=0才會被執(zhí)行;

Quartz

以上介紹的幾種定時(shí)器都是進(jìn)程內(nèi)的調(diào)度,而Quartz提供了分布式調(diào)度,所有被調(diào)度的任務(wù)都可以存放到數(shù)據(jù)庫中,每個(gè)業(yè)務(wù)節(jié)點(diǎn)通過搶占式的方式去獲取需要執(zhí)行的任務(wù),其中一個(gè)節(jié)點(diǎn)出現(xiàn)問題并不影響任務(wù)的調(diào)度;

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"destroy-method="close"><property name="driverClass" value="com.mysql.jdbc.Driver" /><property name="jdbcUrl" value="jdbc:mysql://localhost:3306/quartz" /><property name="user" value="root" /><property name="password" value="root" /></bean><bean id="scheduler"class="org.springframework.scheduling.quartz.SchedulerFactoryBean"><property name="schedulerName" value="myScheduler"></property><property name="dataSource" ref="dataSource" /><property name="configLocation" value="classpath:quartz.properties" /><property name="triggers"><list><ref bean="firstCronTrigger" /></list></property></bean>

更多關(guān)于Quartz的介紹可以參考本人之前的文章:

  • Spring整合Quartz分布式調(diào)度

  • Quartz數(shù)據(jù)庫表分析

  • Quartz調(diào)度源碼分析

  • 基于Netty+Zookeeper+Quartz調(diào)度分析

當(dāng)然Quartz本身也有不足的地方:底層調(diào)度依賴數(shù)據(jù)庫的悲觀鎖,誰先搶到誰調(diào)度,這樣會導(dǎo)致節(jié)點(diǎn)負(fù)載不均衡;還有調(diào)度和執(zhí)行耦合在一起,導(dǎo)致調(diào)度器會受到業(yè)務(wù)的影響;

Xxl-job/Elastic-job

正因?yàn)镼uartz存在著很多不足的地方,基于Quartz實(shí)現(xiàn)的分布式調(diào)度解決方案出現(xiàn)了包括Xxl-job/Elastic-job等;
整體思路:調(diào)度器和執(zhí)行器拆成不同的進(jìn)程,調(diào)度器還是依賴Quartz本身的調(diào)度方式,但是調(diào)度的并不是具體業(yè)務(wù)的QuartzJobBean,而是統(tǒng)一的一個(gè)RemoteQuartzJobBean,在此Bean中通過遠(yuǎn)程調(diào)用執(zhí)行器去執(zhí)行具體業(yè)務(wù)Bean;具體的執(zhí)行器在啟動時(shí)注冊到注冊中心(如Zookeeper)中,調(diào)度器可以在注冊中心(如Zookeeper)獲取執(zhí)行器信息,并通過相關(guān)的負(fù)載算法指定具體的執(zhí)行器去執(zhí)行;
還提供了運(yùn)維管理界面,可以管理任務(wù),比如像xxl-job:
大數(shù)據(jù)開發(fā)中定時(shí)器有哪些

當(dāng)然還有更多其他的功能,此處就不在介紹了,可以直接去查看官網(wǎng);

選擇合適的定時(shí)器

其實(shí)整體可以分為兩大類:進(jìn)程內(nèi)定時(shí)器包括和分布式調(diào)度器;
進(jìn)程內(nèi)定時(shí)器:Timer,ScheduledExecutorService,Spring Scheduler,HashedWheelTimer(時(shí)間輪);
分布式調(diào)度器:Quartz,Xxl-job/Elastic-job;
所以首先根據(jù)需要僅僅只需要進(jìn)程內(nèi)的定時(shí)器,還是需要分布式調(diào)度;
其次在進(jìn)程內(nèi)Timer基本可以被淘汰了,完全可以使用ScheduledExecutorService來代替,如果系統(tǒng)使用了Spring那當(dāng)然應(yīng)該使用Spring Scheduler;
下面重點(diǎn)看看ScheduledExecutorService和HashedWheelTimer,ScheduledExecutorService內(nèi)部使用的是DelayedWorkQueue,任務(wù)的新增、刪除會導(dǎo)致性能下降;而HashedWheelTimer并不受任務(wù)數(shù)量限制,所以如果任務(wù)很多并且任務(wù)執(zhí)行時(shí)間很短比如心跳,那么HashedWheelTimer是最好的選擇;HashedWheelTimer是單線程的,如果任務(wù)不多并且執(zhí)行時(shí)間過長,影響精確度,而ScheduledExecutorService可以使用多線程這時(shí)候選擇ScheduledExecutorService更好;
最后分布式調(diào)度器里面Quartz和Xxl-job/Elastic-job,對分布式調(diào)度要求不高的情況下才會選擇Quartz,不然都應(yīng)該選擇Xxl-job/Elastic-job

感謝各位的閱讀!關(guān)于“大數(shù)據(jù)開發(fā)中定時(shí)器有哪些”這篇文章就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,讓大家可以學(xué)到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI