Spring任務(wù)
Spring定時(shí)任務(wù)為您在Java體系下提供了便捷的定時(shí)任務(wù)開(kāi)放方式,但其便捷的同時(shí)也有很多企業(yè)化場(chǎng)景下的局限性。通過(guò)對(duì)接SchedulerX任務(wù)調(diào)度,可快速實(shí)現(xiàn)企業(yè)化運(yùn)用的支持。
前提條件
客戶端1.8.13及以上版本
采用Spring Boot模式接入SchedulerX平臺(tái)具體操作,請(qǐng)參見(jiàn)Spring Boot應(yīng)用接入SchedulerX。
接入指南
步驟一:添加pom依賴(lài)
以Spring Boot接入模式為例,應(yīng)用程序的pom.xml文件中添加依賴(lài)及啟動(dòng)類(lèi)。
schedulerx2.version
使用客戶端最新版本。更多信息,請(qǐng)參見(jiàn)客戶端發(fā)布記錄。
<dependency>
<groupId>com.aliyun.schedulerx</groupId>
<artifactId>schedulerx2-spring-boot-starter</artifactId>
<version>${schedulerx2.version}</version>
<!--如果用的是logback,需要把log4j和log4j2排掉 -->
<exclusions>
<exclusion>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
</exclusion>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
</exclusions>
</dependency>
無(wú)論是已經(jīng)使用Spring定時(shí)任務(wù)或初次使用,都需要在啟用類(lèi)上保持@EnableScheduling
注解開(kāi)啟。如下所示:
@SpringBootApplication
@EnableScheduling /** 開(kāi)啟Spring定時(shí)任務(wù) */
public class SchedulerXWorkerApplication {
public static void main(String[] args) {
SpringApplication.run(SchedulerXWorkerApplication.class, args);
}
}
/** Spring原生配置的定時(shí)任務(wù)類(lèi)*/
@Service
public class SpringScheduledProcessor {
@Scheduled(cron = "0/2 * * * * ?")
public void hello() {
logger.info(DateUtil.now() + " hello world. start");
logger.info(DateUtil.now() + " hello world. end");
}
}
對(duì)于新接入上述配置或已經(jīng)滿足上述配置的用戶,SchedulerX 默認(rèn)不會(huì)主動(dòng)接管業(yè)務(wù)應(yīng)用中原本的Spring定時(shí)任務(wù),相應(yīng)定時(shí)任務(wù)還是會(huì)由Spring容器進(jìn)行調(diào)度,不影響原本已有的Spring定時(shí)任務(wù)運(yùn)行。
步驟二:添加配置參數(shù)
后續(xù)業(yè)務(wù)需要讓SchedulerX任務(wù)調(diào)度平臺(tái)來(lái)接管Spring定時(shí)任務(wù)的運(yùn)行,則可以在Properties文件中添加如下配置。
# 1、應(yīng)用接入配置
spring.schedulerx2.endpoint=${endpoint}
spring.schedulerx2.namespace=${namespace}
spring.schedulerx2.groupId=${groupId}
spring.schedulerx2.appKey=${appKey}
# 2、啟用SchedulerX接管Spring定時(shí)任務(wù)
spring.schedulerx2.task.scheduling.scheduler=schedulerx
# 3、SchedulerX接管Spring定時(shí)任務(wù),開(kāi)啟自動(dòng)同步任務(wù);以下非必選參數(shù)
#spring.schedulerx2.task.scheduling.sync=true
#spring.schedulerx2.regionId=同步至目標(biāo)區(qū)域編號(hào)(關(guān)于RegionId請(qǐng)參見(jiàn)服務(wù)接入點(diǎn))
#spring.schedulerx2.aliyunAccessKey=XXXXXXXXX
#spring.schedulerx2.aliyunSecretKey=XXXXXXXXX
配置說(shuō)明:
應(yīng)用接入配置:登錄分布式任務(wù)調(diào)度平臺(tái),在左側(cè)導(dǎo)航欄,單擊應(yīng)用管理,在應(yīng)用管理頁(yè)面下的操作欄選擇已創(chuàng)建的應(yīng)用,單擊接入配置即可獲得接入配置信息,如果是首次新接入需要?jiǎng)?chuàng)建一個(gè)應(yīng)用分組。
開(kāi)啟自動(dòng)同步任務(wù)配置:對(duì)于原本已使用Spring定時(shí)任務(wù)且存量定時(shí)任務(wù)較多的用戶,可以選擇在應(yīng)用配置文件中開(kāi)啟自動(dòng)同步任務(wù),大幅度簡(jiǎn)化步驟三中的手動(dòng)創(chuàng)建過(guò)程。配置中的RegionId請(qǐng)參見(jiàn)服務(wù)接入點(diǎn)中的地域ID。
自動(dòng)同步的任務(wù)為了保持與原生Spring任務(wù)在集群環(huán)境中運(yùn)行的規(guī)則一致,默認(rèn)同步至SchedulerX平臺(tái)上的任務(wù)執(zhí)行模式為廣播運(yùn)行(即集群中每臺(tái)機(jī)器都會(huì)在相應(yīng)時(shí)點(diǎn)執(zhí)行該任務(wù))。如果業(yè)務(wù)需要自動(dòng)在集群機(jī)器中選擇一個(gè)運(yùn)行,可在控制臺(tái)編輯相應(yīng)任務(wù)的執(zhí)行模式為單機(jī)運(yùn)行。關(guān)于參數(shù)的詳細(xì)信息,請(qǐng)參見(jiàn)步驟三。
步驟三:手動(dòng)創(chuàng)建定時(shí)任務(wù)(可選)
如果您在步驟二中配置了開(kāi)啟自動(dòng)同步任務(wù),則不需要手動(dòng)創(chuàng)建定時(shí)任務(wù)。
在左側(cè)導(dǎo)航欄,單擊任務(wù)管理。
在任務(wù)管理頁(yè)面,單擊創(chuàng)建任務(wù)。選擇SpringSchedule任務(wù)類(lèi)型,配置對(duì)應(yīng)定時(shí)任務(wù)類(lèi)及其方法名。
配置名稱(chēng)
意義
任務(wù)名
任務(wù)名稱(chēng)。
描述
任務(wù)描述,盡量簡(jiǎn)潔地描述業(yè)務(wù),便于后續(xù)搜索。
應(yīng)用ID
任務(wù)所屬分組。可以在下拉列表中選擇。
任務(wù)類(lèi)型
指任務(wù)所實(shí)現(xiàn)的語(yǔ)言,當(dāng)前支持Java、Shell、Python、Go、HTTP、Node.js、XXL-JOB和DataWorks類(lèi)型,其中Shell、Python和Go會(huì)彈出編輯框,在編輯框中編寫(xiě)任務(wù)腳本。
本文任務(wù)類(lèi)型為SpringSchedule。
spring schedule配置
定時(shí)任務(wù)代碼的完整類(lèi)名(class name)和任務(wù)的方法名(method name)。
執(zhí)行模式
執(zhí)行模式,這里特指任務(wù)執(zhí)行的模式,當(dāng)前支持以下模式。
單機(jī)運(yùn)行:隨機(jī)選一臺(tái)機(jī)器執(zhí)行。
廣播運(yùn)行:所有機(jī)器同時(shí)執(zhí)行并等待全部結(jié)束。
說(shuō)明當(dāng)選擇了不同的執(zhí)行模式后,高級(jí)設(shè)置中的參數(shù)會(huì)相應(yīng)變化。
優(yōu)先級(jí)
同一應(yīng)用下多個(gè)任務(wù)同時(shí)在一個(gè)實(shí)例中運(yùn)行時(shí),優(yōu)先級(jí)高的任務(wù)會(huì)被優(yōu)先執(zhí)行。但當(dāng)一個(gè)應(yīng)用中的多個(gè)任務(wù)在多個(gè)實(shí)例中運(yùn)行時(shí),不同優(yōu)先級(jí)的任務(wù)被調(diào)度到不同實(shí)例執(zhí)行,可能導(dǎo)致低優(yōu)先級(jí)任務(wù)被優(yōu)先執(zhí)行。SchedulerX通過(guò)可搶占的優(yōu)先級(jí)隊(duì)列規(guī)避了這種可能性,并保證同時(shí)在池子中等待的高優(yōu)先級(jí)任務(wù)被優(yōu)先執(zhí)行。更多信息,請(qǐng)參見(jiàn)可搶占的優(yōu)先級(jí)隊(duì)列。
任務(wù)參數(shù)
任意字符串,可以在運(yùn)行時(shí)通過(guò)上下文獲取。
配置對(duì)應(yīng)定時(shí)觸發(fā)頻率。
說(shuō)明頻率會(huì)以控制臺(tái)配置的頻率為準(zhǔn),Spring定時(shí)任務(wù)代碼中原生注解
@Scheduled
中配置將會(huì)失效,但該注解在代碼中需要保留。定時(shí)參數(shù)說(shuō)明如下:
配置名稱(chēng)
意義
時(shí)間類(lèi)型
none:無(wú)調(diào)度方式,一般通過(guò)工作流觸發(fā)。
cron:Cron表達(dá)式。
api:通過(guò)API觸發(fā)。
fixed_rate:固定頻率。
second_delay:秒級(jí)固定延遲。
one_time:一次性任務(wù)。
cron表達(dá)式(僅適用于cron時(shí)間類(lèi)型)
填寫(xiě)Cron表達(dá)式。可以直接按照Cron語(yǔ)法填寫(xiě),也可以使用工具生成并驗(yàn)證。
固定頻率(僅適用于fixed_rate時(shí)間類(lèi)型)
填寫(xiě)固定頻率,單位為秒,只支持60秒以上。例如200表示每200s調(diào)度一次。
固定延遲(僅適用于second_delay時(shí)間類(lèi)型)
填寫(xiě)固定延遲,單位為秒。范圍為1秒~60秒。例如5表示延遲5秒觸發(fā)調(diào)度。
高級(jí)配置參數(shù)說(shuō)明如下:
配置名稱(chēng)
意義
數(shù)據(jù)時(shí)間偏移
數(shù)據(jù)時(shí)間相對(duì)于調(diào)度時(shí)間的偏移,可以在調(diào)度時(shí)從上下文獲取該值。
時(shí)區(qū)
可以根據(jù)實(shí)際情況選擇不同時(shí)區(qū),包括一些常用國(guó)家或地區(qū),也包括標(biāo)準(zhǔn)的GMT表達(dá)方式。
日歷
可選擇工作日或者金融日。
設(shè)置相關(guān)報(bào)警條件和通知渠道等。關(guān)于通知渠道,請(qǐng)參見(jiàn)通知聯(lián)系人和通知聯(lián)系人組。
完成上述步驟后,SchedulerX 任務(wù)調(diào)度平臺(tái)即可接管運(yùn)行Spring的定時(shí)任務(wù),支持為原生的Spring任務(wù)帶來(lái)的各種可視化管控、任務(wù)業(yè)務(wù)日志查詢(xún)、執(zhí)行鏈路查看、任務(wù)執(zhí)行通知報(bào)警等企業(yè)級(jí)能力。
步驟四:驗(yàn)證接入任務(wù)
啟動(dòng)Spring應(yīng)用,啟動(dòng)成功之后。登錄分布式任務(wù)調(diào)度平臺(tái),在左側(cè)導(dǎo)航欄單擊應(yīng)用管理,查看如下對(duì)應(yīng)應(yīng)用分組的接入實(shí)例是否存在,即可說(shuō)明已成功完成應(yīng)用接入平臺(tái)。
在控制臺(tái)左側(cè)導(dǎo)航欄單擊任務(wù)管理,選擇操作欄對(duì)應(yīng)的應(yīng)用任務(wù)單擊運(yùn)行一次,運(yùn)行成功即可。
常見(jiàn)問(wèn)題
SchedulerX接管后原Spring定時(shí)器依舊運(yùn)行
由于應(yīng)用中配置了自定義的Scheduler調(diào)度器導(dǎo)致SchedulerX覆蓋自定義處理器。請(qǐng)排查業(yè)務(wù)應(yīng)用工程中是否存在實(shí)現(xiàn)org.springframework.scheduling.annotation.SchedulingConfigurer
接口的類(lèi),確認(rèn)是否調(diào)用了ScheduledTaskRegistrar的setScheduler方法覆蓋默認(rèn)調(diào)度器,如果存在,請(qǐng)注釋掉相關(guān)邏輯即可。
Spring任務(wù)如何獲取任務(wù)上下文
在業(yè)務(wù)應(yīng)用工程代碼中增加以下代碼獲取任務(wù)上下文。
JobContext jobContext = ContainerFactory.getContainerPool().getContext();
Spring任務(wù)是否支持返回結(jié)果
版本客戶端大于1.10.11時(shí),Spring任務(wù)支持返回結(jié)果,您可以直接在定時(shí)方法上返回任意結(jié)果。
@Scheduled(cron = "0/5 * * * * ?")
public ProcessResult helloStandalone1() {
try {
logger.info(DateUtil.now() + " " + Thread.currentThread().getName() + " hello world. start");
TimeUnit.SECONDS.sleep(2L);
logger.info(DateUtil.now() + " " + Thread.currentThread().getName() + " hello world. end");
} catch (Exception e) {
e.printStackTrace();
logger.info(DateUtil.now() + " " + Thread.currentThread().getName() + " hello world. exception end..");
}
return new ProcessResult(true, "執(zhí)行結(jié)果信息");
}
@Scheduled(cron = "0/5 * * * * ?")
public String helloStandalone2() {
try {
logger.info(DateUtil.now() + " " + Thread.currentThread().getName() + " hello world. start");
TimeUnit.SECONDS.sleep(2L);
logger.info(DateUtil.now() + " " + Thread.currentThread().getName() + " hello world. end");
} catch (Exception e) {
e.printStackTrace();
logger.info(DateUtil.now() + " " + Thread.currentThread().getName() + " hello world. exception end..");
}
return "執(zhí)行結(jié)果信息";
}