自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

學(xué)會(huì)這10種定時(shí)任務(wù),我有點(diǎn)飄了

開(kāi)發(fā) 前端
我今天給大家總結(jié)10種非常實(shí)用的定時(shí)任務(wù),總有一種是適合你的。

[[392252]]

前言

最近有幾個(gè)讀者私信給我,問(wèn)我他們的業(yè)務(wù)場(chǎng)景,要用什么樣的定時(shí)任務(wù)。確實(shí),在不用的業(yè)務(wù)場(chǎng)景下要用不同的定時(shí)任務(wù),其實(shí)我們的選擇還是挺多的。我今天給大家總結(jié)10種非常實(shí)用的定時(shí)任務(wù),總有一種是適合你的。

一. Linux自帶的定時(shí)任務(wù)

crontab

不知道你有沒(méi)有遇到過(guò)這種場(chǎng)景:有時(shí)需要臨時(shí)統(tǒng)計(jì)線上的數(shù)據(jù),然后導(dǎo)出到excel表格中。這種需求有時(shí)較為復(fù)雜,光靠寫(xiě)sql語(yǔ)句是無(wú)法滿(mǎn)足需求的,這就需要寫(xiě)java代碼了。然后將該程序打成一個(gè)jar包,在線上環(huán)境執(zhí)行,最后將生成的excel文件下載到本地。

為了減小對(duì)線上環(huán)境的影響,我們一般會(huì)選擇在凌晨1-2點(diǎn),趁用戶(hù)量少的時(shí)候,執(zhí)行統(tǒng)計(jì)程序。(其實(shí)凌晨4點(diǎn)左右,用戶(hù)才是最少的)

由于時(shí)間太晚了,我們完全沒(méi)必要守在那里等執(zhí)行結(jié)果,一個(gè)定時(shí)任務(wù)就能可以搞定。

那么,這種情況用哪種定時(shí)任務(wù)更合適呢?

答案是:linux系統(tǒng)的crontab。(不過(guò)也不排除有些項(xiàng)目沒(méi)部署在linux系統(tǒng)中)

運(yùn)行crontab -e,可以編輯定時(shí)器,然后加入如下命令:

  1. 0 2 * * * /usr/local/java/jdk1.8/bin/java -jar /data/app/tool.jar > /logs/tool.log & 

就可以在每天凌晨2點(diǎn),定時(shí)執(zhí)行tool.jar程序,并且把日志輸出到tool.log文件中。當(dāng)然你也可以把后面的執(zhí)行java程序的命令寫(xiě)成shell腳本,更方便維護(hù)。

使用這種定時(shí)任務(wù)支持方便修改定時(shí)規(guī)則,有界面可以統(tǒng)一管理配置的各種定時(shí)腳本。

crontab命令的基本格式如下:

  1. crontab [參數(shù)] [文件名] 

如果沒(méi)有指定文件名,則接收鍵盤(pán)上輸入的命令,并將它載入到crontab。

參數(shù)功能對(duì)照表如下:

以上參數(shù),如果沒(méi)有使用-u指定用戶(hù),則默認(rèn)使用的當(dāng)前用戶(hù)。

通過(guò)crontab -e命令編輯文件內(nèi)容,具體語(yǔ)法如下:

  1. [分] [小時(shí)] [日期] [月] [星期] 具體任務(wù) 

其中:

  • 分,表示多少分鐘,范圍:0-59
  • 小時(shí),表示多少小時(shí),范圍:0-23
  • 日期,表示具體在哪一天,范圍:1-31
  • 月,表示多少月,范圍:1-12
  • 星期,表示多少周,范圍:0-7,0和7都代表星期日

還有一些特殊字符,比如:

  • *代表如何時(shí)間,比如:*1*** 表示每天凌晨1點(diǎn)執(zhí)行。
  • /代表每隔多久執(zhí)行一次,比如:*/5 **** 表示每隔5分鐘執(zhí)行一次。
  • ,代表支持多個(gè),比如:10 7,9,12 *** 表示在每天的7、9、12點(diǎn)10分各執(zhí)行一次。
  • -代表支持一個(gè)范圍,比如:10 7-9 *** 表示在每天的7、8、9點(diǎn)10分各執(zhí)行一次。

此外,順便說(shuō)一下crontab需要crond服務(wù)支持,crond是linux下用來(lái)周期地執(zhí)行某種任務(wù)的一個(gè)守護(hù)進(jìn)程,在安裝linux操作系統(tǒng)后,默認(rèn)會(huì)安裝crond服務(wù)工具,且crond服務(wù)默認(rèn)就是自啟動(dòng)的。crond進(jìn)程每分鐘會(huì)定期檢查是否有要執(zhí)行的任務(wù),如果有,則會(huì)自動(dòng)執(zhí)行該任務(wù)。

可以通過(guò)以下命令操作相關(guān)服務(wù):

  1. service crond status // 查看運(yùn)行狀態(tài) 
  2. service crond start //啟動(dòng)服務(wù) 
  3. service crond stop //關(guān)閉服務(wù) 
  4. service crond restart //重啟服務(wù) 
  5. service crond reload //重新載入配置 

使用crontab的優(yōu)缺點(diǎn):

  • 優(yōu)點(diǎn):方便修改定時(shí)規(guī)則,支持一些較復(fù)雜的定時(shí)規(guī)則,通過(guò)文件可以統(tǒng)一管理配好的各種定時(shí)腳本。
  • 缺點(diǎn):如果定時(shí)任務(wù)非常多,不太好找,而且必須要求操作系統(tǒng)是linux,否則無(wú)法執(zhí)行。

二. jdk自帶的定時(shí)任務(wù)

1.Thread

各位親愛(ài)的朋友,你沒(méi)看錯(cuò),Thread類(lèi)真的能做定時(shí)任務(wù)。如果你看過(guò)一些定時(shí)任務(wù)框架的源碼,你最后會(huì)發(fā)現(xiàn),它們的底層也會(huì)使用Thread類(lèi)。

實(shí)現(xiàn)這種定時(shí)任務(wù)的具體代碼如下:

  1. public static void init() { 
  2.     new Thread(() -> { 
  3.         while (true) { 
  4.             try { 
  5.                 System.out.println("doSameThing"); 
  6.                 Thread.sleep(1000 * 60 * 5); 
  7.             } catch (Exception e) { 
  8.                 log.error(e); 
  9.             } 
  10.         } 
  11.     }).start(); 

使用Thread類(lèi)可以做最簡(jiǎn)單的定時(shí)任務(wù),在run方法中有個(gè)while的死循環(huán)(當(dāng)然還有其他方式),執(zhí)行我們自己的任務(wù)。有個(gè)需要特別注意的地方是,需要用try...catch捕獲異常,否則如果出現(xiàn)異常,就直接退出循環(huán),下次將無(wú)法繼續(xù)執(zhí)行了。

這種方式做的定時(shí)任務(wù),只能周期性執(zhí)行,不能支持定時(shí)在某個(gè)時(shí)間點(diǎn)執(zhí)行。

此外,該線程可以定義成守護(hù)線程,在后臺(tái)默默執(zhí)行就好。

使用場(chǎng)景:比如項(xiàng)目中有時(shí)需要每隔10分鐘去下載某個(gè)文件,或者每隔5分鐘去讀取模板文件生成靜態(tài)html頁(yè)面等等,一些簡(jiǎn)單的周期性任務(wù)場(chǎng)景。

使用Thread類(lèi)的優(yōu)缺點(diǎn):

  • 優(yōu)點(diǎn):這種定時(shí)任務(wù)非常簡(jiǎn)單,學(xué)習(xí)成本低,容易入手,對(duì)于那些簡(jiǎn)單的周期性任務(wù),是個(gè)不錯(cuò)的選擇。
  • 缺點(diǎn):不支持指定某個(gè)時(shí)間點(diǎn)執(zhí)行任務(wù),不支持延遲執(zhí)行等操作,功能過(guò)于單一,無(wú)法應(yīng)對(duì)一些較為復(fù)雜的場(chǎng)景。

2.TimerTimer

類(lèi)是jdk專(zhuān)門(mén)提供的定時(shí)器工具,用來(lái)在后臺(tái)線程計(jì)劃執(zhí)行指定任務(wù),在java.util包下,要跟TimerTask一起配合使用。

Timer類(lèi)其實(shí)是一個(gè)任務(wù)調(diào)度器,它里面包含了一個(gè)TimerThread線程,在這個(gè)線程中無(wú)限循環(huán)從TaskQueue中獲取TimerTask(該類(lèi)實(shí)現(xiàn)了Runnable接口),調(diào)用其run方法,就能異步執(zhí)行定時(shí)任務(wù)。我們需要繼承TimerTask類(lèi),實(shí)現(xiàn)它的run方法,在該方法中加上自己的業(yè)務(wù)邏輯。

實(shí)現(xiàn)這種定時(shí)任務(wù)的具體代碼如下:

  1. public class TimerTest { 
  2.  
  3.     public static void main(String[] args) { 
  4.         Timer timer = new Timer(); 
  5.         timer.schedule(new TimerTask() { 
  6.             @Override 
  7.             public void run() { 
  8.                 System.out.println("doSomething"); 
  9.             } 
  10.         },2000,1000); 
  11.     } 

先實(shí)例化一個(gè)Timer類(lèi),然后調(diào)用它的schedule方法,在該方法中實(shí)例化TimerTask類(lèi),業(yè)務(wù)邏輯寫(xiě)在run方法中。schedule方法最后的兩次參數(shù)分別表示:延遲時(shí)間 和 間隔時(shí)間,單位是毫秒。上面例子中,設(shè)置的定時(shí)任務(wù)是每隔1秒執(zhí)行一次,延遲2秒執(zhí)行。

主要包含6個(gè)方法:

  • schedule(TimerTask task, Date time), 指定任務(wù)task在指定時(shí)間time執(zhí)行
  • schedule(TimerTask task, long delay), 指定任務(wù)task在指定延遲delay后執(zhí)行
  • schedule(TimerTask task, Date firstTime,long period),指定任務(wù)task在指定時(shí)間firstTime執(zhí)行后,進(jìn)行重復(fù)固定延遲頻率peroid的執(zhí)行
  • schedule(TimerTask task, long delay, long period), 指定任務(wù)task 在指定延遲delay 后,進(jìn)行重復(fù)固定延遲頻率peroid的執(zhí)行
  • scheduleAtFixedRate(TimerTask task,Date firstTime,long period), 指定任務(wù)task在指定時(shí)間firstTime執(zhí)行后,進(jìn)行重復(fù)固定延遲頻率peroid的執(zhí)行
  • scheduleAtFixedRate(TimerTask task, long delay, long period), 指定任務(wù)task 在指定延遲delay 后,進(jìn)行重復(fù)固定延遲頻率peroid的執(zhí)行

不過(guò)使用Timer實(shí)現(xiàn)定時(shí)任務(wù)有以下問(wèn)題:

  1. 由于Timer是單線程執(zhí)行任務(wù),如果其中一個(gè)任務(wù)耗時(shí)非常長(zhǎng),會(huì)影響其他任務(wù)的執(zhí)行。
  2. 如果TimerTask拋出RuntimeException,Timer會(huì)停止所有任務(wù)的運(yùn)行。

使用Timer類(lèi)的優(yōu)缺點(diǎn):

  • 優(yōu)點(diǎn):非常方便實(shí)現(xiàn)多個(gè)周期性的定時(shí)任務(wù),并且支持延遲執(zhí)行,還支持在指定時(shí)間之后支持,功能還算強(qiáng)大。
  • 缺點(diǎn):如果其中一個(gè)任務(wù)耗時(shí)非常長(zhǎng),會(huì)影響其他任務(wù)的執(zhí)行。并且如果TimerTask拋出RuntimeException,Timer會(huì)停止所有任務(wù)的運(yùn)行,所以阿里巴巴開(kāi)發(fā)者規(guī)范中不建議使用它。

3.ScheduledExecutorService

ScheduledExecutorService是JDK1.5+版本引進(jìn)的定時(shí)任務(wù),該類(lèi)位于java.util.concurrent并發(fā)包下。

ScheduledExecutorService是基于多線程的,設(shè)計(jì)的初衷是為了解決Timer單線程執(zhí)行,多個(gè)任務(wù)之間會(huì)互相影響的問(wèn)題。

它主要包含4個(gè)方法:

  • schedule(Runnable command,long delay,TimeUnit unit),帶延遲時(shí)間的調(diào)度,只執(zhí)行一次,調(diào)度之后可通過(guò)Future.get()阻塞直至任務(wù)執(zhí)行完畢。
  • schedule(Callable callable,long delay,TimeUnit unit),帶延遲時(shí)間的調(diào)度,只執(zhí)行一次,調(diào)度之后可通過(guò)Future.get()阻塞直至任務(wù)執(zhí)行完畢,并且可以獲取執(zhí)行結(jié)果。
  • scheduleAtFixedRate,表示以固定頻率執(zhí)行的任務(wù),如果當(dāng)前任務(wù)耗時(shí)較多,超過(guò)定時(shí)周期period,則當(dāng)前任務(wù)結(jié)束后會(huì)立即執(zhí)行。
  • scheduleWithFixedDelay,表示以固定延時(shí)執(zhí)行任務(wù),延時(shí)是相對(duì)當(dāng)前任務(wù)結(jié)束為起點(diǎn)計(jì)算開(kāi)始時(shí)間。

實(shí)現(xiàn)這種定時(shí)任務(wù)的具體代碼如下:

  1. public class ScheduleExecutorTest { 
  2.  
  3.     public static void main(String[] args) { 
  4.         ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5); 
  5.         scheduledExecutorService.scheduleAtFixedRate(() -> { 
  6.             System.out.println("doSomething"); 
  7.         },1000,1000, TimeUnit.MILLISECONDS); 
  8.     } 

調(diào)用ScheduledExecutorService類(lèi)的scheduleAtFixedRate方法實(shí)現(xiàn)周期性任務(wù),每隔1秒鐘執(zhí)行一次,每次延遲1秒再執(zhí)行。

這種定時(shí)任務(wù)是阿里巴巴開(kāi)發(fā)者規(guī)范中用來(lái)替代Timer類(lèi)的方案,對(duì)于多線程執(zhí)行周期性任務(wù),是個(gè)不錯(cuò)的選擇。

ScheduledExecutorService的優(yōu)缺點(diǎn):

  • 優(yōu)點(diǎn):基于多線程的定時(shí)任務(wù),多個(gè)任務(wù)之間不會(huì)相關(guān)影響,支持周期性的執(zhí)行任務(wù),并且?guī)а舆t功能。
  • 缺點(diǎn):不支持一些較復(fù)雜的定時(shí)規(guī)則。

三. spring支持的定時(shí)任務(wù)

1.spring task

spring task是spring3以上版本自帶的定時(shí)任務(wù),實(shí)現(xiàn)定時(shí)任務(wù)的功能時(shí),需要引入spring-context包,目前它支持:xml 和 注解 兩種方式。

1. 項(xiàng)目實(shí)戰(zhàn)

由于xml方式太古老了,我們以springboot項(xiàng)目中注解方式為例。

第一步,在pom.xml文件中引入spring-context相關(guān)依賴(lài)。

  1. <dependency> 
  2.     <groupId>org.springframework</groupId> 
  3.     <artifactId>spring-context</artifactId> 
  4. </dependency> 

第二步,在springboot啟動(dòng)類(lèi)上加上@EnableScheduling注解。

  1. @EnableScheduling 
  2. @SpringBootApplication 
  3. public class Application { 
  4.  
  5.     public static void main(String[] args) { 
  6.         new SpringApplicationBuilder(Application.class).web(WebApplicationType.SERVLET).run(args); 
  7.     } 

第三步,使用@Scheduled注解定義定時(shí)規(guī)則。

  1. @Service 
  2. public class SpringTaskTest { 
  3.  
  4.     @Scheduled(cron = "${sue.spring.task.cron}"
  5.     public void fun() { 
  6.         System.out.println("doSomething"); 
  7.     } 

第四步,在applicationContext.properties文件中配置參數(shù):

  1. sue.spring.task.cron=*/10 * * * * ? 

這樣就能每隔10秒執(zhí)行一次fun方法了。

2. cron規(guī)則

spring4以上的版本中,cron表達(dá)式包含6個(gè)參數(shù):

  1. [秒] [分] [時(shí)] [日期] [月] [星期] 

還支持幾個(gè)常用的特殊符號(hào):

  • *:表示任何時(shí)間觸發(fā)任務(wù)
  • ,:表示指定的時(shí)間觸發(fā)任務(wù)
  • -:表示一段時(shí)間內(nèi)觸發(fā)任務(wù)
  • /:表示從哪一個(gè)時(shí)刻開(kāi)始,每隔多長(zhǎng)時(shí)間觸發(fā)一次任務(wù)。
  • ?:表示用于月中的天和周中的天兩個(gè)子表達(dá)式,表示不指定值。

cron表達(dá)式參數(shù)具體含義:

  1. 秒,取值范圍:0-59,支持*、,、-、/。
  2. 分,取值范圍:0-59,支持*、,、-、/。
  3. 時(shí),取值范圍:0-23,支持*、,、-、/。
  4. 日期,取值范圍:1-31,支持*、,、-、/。比秒多了?,表示如果指定的星期觸發(fā)了,則配置的日期變成無(wú)效。
  5. 月,取值范圍:1-12,支持*、,、-、/。
  6. 星期,取值范圍:1~7,1代表星期天,6代表星期六,其他的以此類(lèi)推。支持*、,、-、/、?。比秒多了?,表示如果指定的日期觸發(fā)了,則配置的星期變成無(wú)效。

常見(jiàn)cron表達(dá)式使用舉例:

  • 0 0 0 1 * ? 每月1號(hào)零點(diǎn)執(zhí)行
  • 0 0 2 * * ? 每天凌晨2點(diǎn)執(zhí)行
  • 0 0 2 * * ? 每天凌晨2點(diǎn)執(zhí)行
  • 0 0/5 11 * * ? 每天11點(diǎn)-11點(diǎn)55分,每隔5分鐘執(zhí)行一次
  • 0 0 18 ? * WED 每周三下午6點(diǎn)執(zhí)行

spring task先通過(guò)ScheduledAnnotationBeanPostProcessor類(lèi)的processScheduled方法,解析和收集Scheduled注解中的參數(shù),包含:cron表達(dá)式。

然后在ScheduledTaskRegistrar類(lèi)的afterPropertiesSet方法中,默認(rèn)初始化一個(gè)單線程的ThreadPoolExecutor執(zhí)行任務(wù)。

對(duì)spring task感興趣的小伙伴,可以加我微信找我私聊。

使用spring task的優(yōu)缺點(diǎn):

  • 優(yōu)點(diǎn):spring框架自帶的定時(shí)功能,springboot做了非常好的封裝,開(kāi)啟和定義定時(shí)任務(wù)非常容易,支持復(fù)雜的cron表達(dá)式,可以滿(mǎn)足絕大多數(shù)單機(jī)版的業(yè)務(wù)場(chǎng)景。單個(gè)任務(wù)時(shí),當(dāng)前次的調(diào)度完成后,再執(zhí)行下一次任務(wù)調(diào)度。
  • 缺點(diǎn):默認(rèn)單線程,如果前面的任務(wù)執(zhí)行時(shí)間太長(zhǎng),對(duì)后面任務(wù)的執(zhí)行有影響。不支持集群方式部署,不能做數(shù)據(jù)存儲(chǔ)型定時(shí)任務(wù)。

2.spring quart

zquartz是OpenSymphony開(kāi)源組織在Job scheduling領(lǐng)域的開(kāi)源項(xiàng)目,是由java開(kāi)發(fā)的一個(gè)開(kāi)源的任務(wù)日程管理系統(tǒng)。

quartz能做什么?

  • 作業(yè)調(diào)度:調(diào)用各種框架的作業(yè)腳本,例如shell,hive等。
  • 定時(shí)任務(wù):在某一預(yù)定的時(shí)刻,執(zhí)行你想要執(zhí)行的任務(wù)。

架構(gòu)圖如下:

quartz包含的主要接口如下:

  • Scheduler 代表調(diào)度容器,一個(gè)調(diào)度容器中可以注冊(cè)多個(gè)JobDetail和Trigger。
  • Job 代表工作,即要執(zhí)行的具體內(nèi)容。
  • JobDetail 代表具體的可執(zhí)行的調(diào)度程序,Job是這個(gè)可執(zhí)行程調(diào)度程序所要執(zhí)行的內(nèi)容。
  • JobBuilder 用于定義或構(gòu)建JobDetail實(shí)例。
  • Trigger 代表調(diào)度觸發(fā)器,決定什么時(shí)候去調(diào)。
  • TriggerBuilder 用于定義或構(gòu)建觸發(fā)器。
  • JobStore 用于存儲(chǔ)作業(yè)和任務(wù)調(diào)度期間的狀態(tài)。

1. 項(xiàng)目實(shí)戰(zhàn)

我們還是以springboot集成quartz為例。

第一步,在pom.xml文件中引入quartz相關(guān)依賴(lài)。

  1. <dependency> 
  2.     <groupId>org.springframework.boot</groupId> 
  3.     <artifactId>spring-boot-starter-quartz</artifactId> 
  4. </dependency> 

第二步,創(chuàng)建真正的定時(shí)任務(wù)執(zhí)行類(lèi),該類(lèi)繼承QuartzJobBean。

  1. public class QuartzTestJob extends QuartzJobBean { 
  2.     @Override 
  3.     protected void executeInternal(JobExecutionContext context) throws JobExecutionException { 
  4.         String userName = (String) context.getJobDetail().getJobDataMap().get("userName"); 
  5.         System.out.println("userName:" + userName); 
  6.     } 

第三步,創(chuàng)建調(diào)度程序JobDetail和調(diào)度器Trigger。

  1. @Configuration 
  2. public class QuartzConfig { 
  3.     @Value("${sue.spring.quartz.cron}"
  4.     private String testCron; 
  5.  
  6.     /** 
  7.      * 創(chuàng)建定時(shí)任務(wù) 
  8.      */ 
  9.     @Bean 
  10.     public JobDetail quartzTestDetail() { 
  11.         JobDetail jobDetail = JobBuilder.newJob(QuartzTestJob.class) 
  12.                 .withIdentity("quartzTestDetail""QUARTZ_TEST"
  13.                 .usingJobData("userName""susan"
  14.                 .storeDurably() 
  15.                 .build(); 
  16.         return jobDetail; 
  17.     } 
  18.  
  19.     /** 
  20.      * 創(chuàng)建觸發(fā)器 
  21.      */ 
  22.     @Bean 
  23.     public Trigger quartzTestJobTrigger() { 
  24.         //每隔5秒執(zhí)行一次 
  25.         CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(testCron); 
  26.  
  27.         //創(chuàng)建觸發(fā)器 
  28.         Trigger trigger = TriggerBuilder.newTrigger() 
  29.                 .forJob(quartzTestDetail()) 
  30.                 .withIdentity("quartzTestJobTrigger""QUARTZ_TEST_JOB_TRIGGER"
  31.                 .withSchedule(cronScheduleBuilder) 
  32.                 .build(); 
  33.         return trigger
  34.     } 

第四步,在applicationContext.properties文件中配置參數(shù):

  1. sue.spring.quartz.cron=*/5 * * * * ? 

這樣就能每隔5秒執(zhí)行一次QuartzTestJob類(lèi)的executeInternal方法了。

CronTrigger配置格式:

  1. [秒] [分] [小時(shí)] [日] [月] [周] [年] 

spring quartz跟spring task的cron表達(dá)式規(guī)則基本一致,只是spring4以上的版本去掉了后面的年,而quartz的CronTrigger的年是非必填的,這里我就不做過(guò)多介紹了。

使用spring quartz的優(yōu)缺點(diǎn):

  • 優(yōu)點(diǎn):默認(rèn)是多線程異步執(zhí)行,單個(gè)任務(wù)時(shí),在上一個(gè)調(diào)度未完成時(shí),下一個(gè)調(diào)度時(shí)間到時(shí),會(huì)另起一個(gè)線程開(kāi)始新的調(diào)度,多個(gè)任務(wù)之間互不影響。支持復(fù)雜的cron表達(dá)式,它能被集群實(shí)例化,支持分布式部署。
  • 缺點(diǎn):相對(duì)于spring task實(shí)現(xiàn)定時(shí)任務(wù)成本更高,需要手動(dòng)配置QuartzJobBean、JobDetail和Trigger等。需要引入了第三方的quartz包,有一定的學(xué)習(xí)成本。不支持并行調(diào)度,不支持失敗處理策略和動(dòng)態(tài)分片的策略等。

四. 分布式定時(shí)任務(wù)

1.xxl-job

xxl-job是大眾點(diǎn)評(píng)(許雪里)開(kāi)發(fā)的一個(gè)分布式任務(wù)調(diào)度平臺(tái),其核心設(shè)計(jì)目標(biāo)是開(kāi)發(fā)迅速、學(xué)習(xí)簡(jiǎn)單、輕量級(jí)、易擴(kuò)展。現(xiàn)已開(kāi)放源代碼并接入多家公司線上產(chǎn)品線,開(kāi)箱即用。

xxl-job框架對(duì)quartz進(jìn)行了擴(kuò)展,使用mysql數(shù)據(jù)庫(kù)存儲(chǔ)數(shù)據(jù),并且內(nèi)置jetty作為RPC服務(wù)調(diào)用。

主要特點(diǎn)如下:

  1. 有界面維護(hù)定時(shí)任務(wù)和觸發(fā)規(guī)則,非常容易管理。
  2. 能動(dòng)態(tài)啟動(dòng)或停止任務(wù)
  3. 支持彈性擴(kuò)容縮容
  4. 支持任務(wù)失敗報(bào)警
  5. 支持動(dòng)態(tài)分片
  6. 支持故障轉(zhuǎn)移
  7. Rolling實(shí)時(shí)日志
  8. 支持用戶(hù)和權(quán)限管理

管理界面:

整體架構(gòu)圖如下:

使用quartz架構(gòu)圖如下:

項(xiàng)目實(shí)戰(zhàn)

xxl-admin管理后臺(tái)部署和mysql腳本執(zhí)行等這些前期準(zhǔn)備工作,我就不過(guò)多介紹了,有需求的朋友可以找我私聊,這些更偏向于運(yùn)維的事情。

假設(shè)前期工作已經(jīng)OK了,接下來(lái)我們需要:

第一步,在pom.xml文件中引入xxl-job相關(guān)依賴(lài)。

  1. <dependency> 
  2.    <groupId>com.xuxueli</groupId> 
  3.    <artifactId>xxl-job-core</artifactId> 
  4. </dependency> 

第二步,在applicationContext.properties文件中配置參數(shù):

  1. xxl.job.admin.address: http://localhost:8088/xxl-job-admin/ 
  2. xxl.job.executor.appname: xxl-job-executor-sample 
  3. xxl.job.executor.port: 8888 
  4. xxl.job.executor.logpath: /data/applogs/xxl-job/ 

第三步,創(chuàng)建HelloJobHandler類(lèi)繼承IJobHandler類(lèi):

  1. @JobHandler(value = "helloJobHandler"
  2. @Component 
  3. public class HelloJobHandler extends IJobHandler { 
  4.  
  5.     @Override 
  6.     public ReturnT<String> execute(String param) { 
  7.         System.out.println("XXL-JOB, Hello World."); 
  8.         return SUCCESS; 
  9.     } 

這樣定時(shí)任務(wù)就配置好了。

  • 建議把定時(shí)任務(wù)單獨(dú)部署到另外一個(gè)服務(wù)中,跟api服務(wù)分開(kāi)。根據(jù)我以往的經(jīng)驗(yàn),job大部分情況下,會(huì)對(duì)數(shù)據(jù)做批量操作,如果操作的數(shù)據(jù)量太大,可能會(huì)對(duì)服務(wù)的內(nèi)存和cpu資源造成一定的影響。

使用xxl-job的優(yōu)缺點(diǎn):

  • 優(yōu)點(diǎn):有界面管理定時(shí)任務(wù),支持彈性擴(kuò)容縮容、動(dòng)態(tài)分片、故障轉(zhuǎn)移、失敗報(bào)警等功能。它的功能非常強(qiáng)大,很多大廠在用,可以滿(mǎn)足絕大多數(shù)業(yè)務(wù)場(chǎng)景。
  • 缺點(diǎn):和quartz一樣,通過(guò)數(shù)據(jù)庫(kù)分布式鎖,來(lái)控制任務(wù)不能重復(fù)執(zhí)行。在任務(wù)非常多的情況下,有一些性能問(wèn)題。

2.elastic-job

elastic-job是當(dāng)當(dāng)網(wǎng)開(kāi)發(fā)的彈性分布式任務(wù)調(diào)度系統(tǒng),功能豐富強(qiáng)大,采用zookeeper實(shí)現(xiàn)分布式協(xié)調(diào),實(shí)現(xiàn)任務(wù)高可用以及分片。它是專(zhuān)門(mén)為高并發(fā)和復(fù)雜業(yè)務(wù)場(chǎng)景開(kāi)發(fā)。

elastic-job目前是apache的shardingsphere項(xiàng)目下的一個(gè)子項(xiàng)目,官網(wǎng)地址:http://shardingsphere.apache.org/elasticjob/。

elastic-job在2.x之后,出了兩個(gè)產(chǎn)品線:Elastic-Job-Lite和Elastic-Job-Cloud,而我們一般使用Elastic-Job-Lite就能夠滿(mǎn)足需求。Elastic-Job-Lite定位為輕量級(jí)無(wú)中心化解決方案,使用jar包的形式提供分布式任務(wù)的協(xié)調(diào)服務(wù),外部?jī)H依賴(lài)于Zookeeper。。

主要特點(diǎn)如下:

  • 分布式調(diào)度協(xié)調(diào)
  • 彈性擴(kuò)容縮容
  • 失效轉(zhuǎn)移
  • 錯(cuò)過(guò)執(zhí)行作業(yè)重觸發(fā)
  • 作業(yè)分片一致性,保證同一分片在分布式環(huán)境中僅一個(gè)執(zhí)行實(shí)例
  • 自診斷并修復(fù)分布式不穩(wěn)定造成的問(wèn)題
  • 支持并行調(diào)度

整體架構(gòu)圖:

項(xiàng)目實(shí)戰(zhàn)

第一步,在pom.xml文件中引入elastic-job相關(guān)依賴(lài)。

  1. <dependency> 
  2.     <groupId>com.dangdang</groupId> 
  3.     <artifactId>elastic-job-lite-core</artifactId> 
  4. </dependency> 
  5. <dependency> 
  6.     <groupId>com.dangdang</groupId> 
  7.     <artifactId>elastic-job-lite-spring</artifactId> 
  8. </dependency> 

第二步,增加ZKConfig類(lèi),配置zookeeper:

  1. @Configuration 
  2. @ConditionalOnExpression("'${zk.serverList}'.length() > 0"
  3. public class ZKConfig { 
  4.  
  5.     @Bean 
  6.     public ZookeeperRegistryCenter registry(@Value("${zk.serverList}") String serverList, 
  7.                                              @Value("${zk.namespace}") String namespace) { 
  8.         return new ZookeeperRegistryCenter(new ZookeeperConfiguration(serverList, namespace)); 
  9.     } 
  10.  

第三步,定義一個(gè)類(lèi)實(shí)現(xiàn)SimpleJob接口:

  1. public class TestJob implements SimpleJob { 
  2.  
  3.     @Override 
  4.     public void execute(ShardingContext shardingContext){ 
  5.         System.out.println("ShardingTotalCount:"+shardingContext.getShardingTotalCount()); 
  6.         System.out.println("ShardingItem:"+shardingContext.getShardingItem()); 
  7.     } 

第四步,增加JobConfig配置任務(wù):

  1. @Configuration 
  2. public class JobConfig { 
  3.     @Value("${sue.spring.elatisc.cron}"
  4.     private String testCron; 
  5.     @Value("${sue.spring.elatisc.itemParameters}"
  6.     private  String shardingItemParameters; 
  7.     @Value("${sue.spring.elatisc.jobParameters}"
  8.     private String jobParameters =; 
  9.     @Value("${sue.spring.elatisc.shardingTotalCount}"
  10.     private int shardingTotalCount; 
  11.      
  12.     @Autowired 
  13.     private ZookeeperRegistryCenter registryCenter; 
  14.  
  15.     @Bean 
  16.     public SimpleJob testJob() { 
  17.         return new TestJob(); 
  18.     } 
  19.  
  20.     @Bean 
  21.     public JobScheduler simpleJobScheduler(final SimpleJob simpleJob) { 
  22.         return new SpringJobScheduler(simpleJob, registryCenter, getConfiguration(simpleJob.getClass(), 
  23.                 cron, shardingTotalCount, shardingItemParameters, jobParameters)); 
  24.     } 
  25.  
  26.     private geConfiguration getConfiguration(Class<? extends SimpleJob> jobClass,String cron,int shardingTotalCount,String shardingItemParameters,String jobParameters) { 
  27.         JobCoreConfiguration simpleCoreConfig = JobCoreConfiguration.newBuilder(jobClass.getName(), testCron, shardingTotalCount). 
  28.                 shardingItemParameters(shardingItemParameters).jobParameter(jobParameters).build(); 
  29.         SimpleJobConfiguration simpleJobConfig = new SimpleJobConfiguration(simpleCoreConfig, jobClass.getCanonicalName()); 
  30.         LiteJobConfiguration jobConfig = LiteJobConfiguration.newBuilder(simpleJobConfig).overwrite(true).build(); 
  31.         return jobConfig; 
  32.     } 

其中:

  • cron:cron表達(dá)式,定義觸發(fā)規(guī)則。
  • shardingTotalCount:定義作業(yè)分片總數(shù)
  • shardingItemParameters:定義分配項(xiàng)參數(shù),一般用分片序列號(hào)和參數(shù)用等號(hào)分隔,多個(gè)鍵值對(duì)用逗號(hào)分隔,分片序列號(hào)從0開(kāi)始,不可大于或等于作業(yè)分片總數(shù)。
  • jobParameters:作業(yè)自定義參數(shù)

第五步,在applicationContext.properties文件中配置參數(shù):

  1. spring.application.name=elasticjobDemo 
  2. zk.serverList=localhost:2181 
  3. zk.namespace=elasticjobDemo 
  4. sue.spring.elatisc.cron=0/5 * * * * ? 
  5. sue.spring.elatisc.itemParameters=0=A,1=B,2=C,3=D 
  6. sue.spring.elatisc.jobParameters=test 
  7. sue.spring.elatisc.shardingTotalCount=4 

這樣定時(shí)任務(wù)就配置好了,創(chuàng)建定時(shí)任務(wù)的步驟,相對(duì)于xxl-job來(lái)說(shuō)要繁瑣一些。

使用elastic-job的優(yōu)缺點(diǎn):

  • 優(yōu)點(diǎn):支持分布式調(diào)度協(xié)調(diào),支持分片,適合高并發(fā),和一些業(yè)務(wù)相對(duì)來(lái)說(shuō)較復(fù)雜的場(chǎng)景。
  • 缺點(diǎn):需要依賴(lài)于zookeeper,實(shí)現(xiàn)定時(shí)任務(wù)相對(duì)于xxl-job要復(fù)雜一些,要對(duì)分片規(guī)則非常熟悉。

3.其他分布式定時(shí)任務(wù)

1. Saturn

Saturn是唯品會(huì)開(kāi)源的一個(gè)分布式任務(wù)調(diào)度平臺(tái)。取代傳統(tǒng)的Linux Cron/Spring Batch Job的方式,做到全域統(tǒng)一配置,統(tǒng)一監(jiān)控,任務(wù)高可用以及分片并發(fā)處理。

Saturn是在當(dāng)當(dāng)開(kāi)源的Elastic-Job基礎(chǔ)上,結(jié)合各方需求和我們的實(shí)踐見(jiàn)解改良而成。使用案例:唯品會(huì)、酷狗音樂(lè)、新網(wǎng)銀行、海融易、航美在線、量富征信等。

github地址:https://github.com/vipshop/Saturn/

2. TBSchedule

TBSchedule是阿里開(kāi)發(fā)的一款分布式任務(wù)調(diào)度平臺(tái),旨在將調(diào)度作業(yè)從業(yè)務(wù)系統(tǒng)中分離出來(lái),降低或者是消除和業(yè)務(wù)系統(tǒng)的耦合度,進(jìn)行高效異步任務(wù)處理。

目前被廣泛應(yīng)用在阿里巴巴、淘寶、支付寶、京東、聚美、汽車(chē)之家、國(guó)美等很多互聯(lián)網(wǎng)企業(yè)的流程調(diào)度系統(tǒng)中。

github地址:https://github.com/taobao/TBSchedule

老實(shí)說(shuō)優(yōu)秀的定時(shí)任務(wù)還是挺多的,不是說(shuō)哪種定時(shí)任務(wù)牛逼我們就一定要用哪種,而是要根據(jù)實(shí)際業(yè)務(wù)需求選擇。每種定時(shí)任務(wù)都有優(yōu)缺點(diǎn),合理選擇既能滿(mǎn)足業(yè)務(wù)需求,又能避免資源浪費(fèi),才是上上策。當(dāng)然在實(shí)際的業(yè)務(wù)場(chǎng)景,通常會(huì)多種定時(shí)任務(wù)一起配合使用。

希望我們能夠共同進(jìn)步,一起成長(zhǎng)。

責(zé)任編輯:姜華 來(lái)源: 蘇三說(shuō)技術(shù)
相關(guān)推薦

2020-01-18 11:13:08

CPU程序存儲(chǔ)

2020-12-08 09:45:51

項(xiàng)目Apache分表

2024-01-22 08:53:00

策略任務(wù)RocketMQ

2021-11-22 12:35:40

Python命令定時(shí)任務(wù)

2024-02-26 11:12:33

定時(shí)任務(wù)線程

2024-01-31 08:38:57

Python定時(shí)任務(wù)函數(shù)

2021-09-26 09:17:01

Python命令定時(shí)任務(wù)

2025-02-24 16:00:00

SpringBoot定時(shí)任務(wù)開(kāi)發(fā)

2017-03-13 09:12:00

TCP數(shù)據(jù)結(jié)構(gòu)請(qǐng)求包

2009-10-28 10:05:29

Ubuntucrontab定時(shí)任務(wù)

2012-02-07 13:31:14

SpringJava

2025-03-12 09:54:02

2025-01-08 09:55:37

Spring接口數(shù)據(jù)庫(kù)

2010-03-10 15:47:58

crontab定時(shí)任務(wù)

2021-06-30 07:19:34

SpringBoot定時(shí)任務(wù)

2024-09-20 05:49:04

SpringBoot后端

2024-11-04 16:01:01

2024-12-27 08:24:55

2010-01-07 13:38:41

Linux定時(shí)任務(wù)

2020-12-21 07:31:23

實(shí)現(xiàn)單機(jī)JDK
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)