一文帶你吃透定時任務框架
一、介紹
說到定時任務,相信大家都不陌生,在我們實際的工作中,用到定時任務的場景可以說非常的多,例如:
- 雙 11 的 0 點,定時開啟秒殺
- 每月1號,財務系統自動拉取每個人的績效工資,用于薪資計算
- 使用 TCP 長連接時,客戶端按照固定頻率定時向服務端發送心跳請求
等等,定時器像水和空氣一般,普遍存在于各個場景中,在實際的業務開發中,基本上少不了定時任務的應用。
總結起來,一般定時任務的表現有以下幾個特征:
1.在某個時刻觸發,例如11.11號0點開啟秒殺
2.按照固定頻率周期性觸發,例如每分鐘發送心跳請求
3.預約指定時刻開始周期性觸發,例如從12.1號開始每天7點鬧鐘響起
說了這么多,也不BB了,下面我們就來點干活,列舉一些實際項目中使用的相關工具!
二、crontab 定時器
2.1、介紹
crontab 嚴格來說并不是屬于 java 內的,它是 linux 自帶的一個工具,可以周期性地執行某個shell腳本或命令。
由于 crontab 在實際開發中應用比較多, 特別是對于運維的人,crontab 命令是必須用到的命令,自動化運維中一定少不了它,而且 crontab 表達式跟我們后面要介紹的其他定時任務框架,例如 Quartz,Spring Schedule 的 cron 表達式類似,所以這里先介紹 crontab。
簡而言之,crontab 就是一個自定義定時器。
命令格式如下:
.---------------- minute (0 - 59)
| .------------- hour (0 - 23)
| | .---------- day of month (1 - 31)
| | | .------- month (1 - 12) OR jan,feb,mar,apr ...
| | | | .---- day of week (0 - 6) (Sunday=0 or 7) ...
| | | | |
* * * * * command
- 第一個參數(minute):代表一小時內的第幾分,范圍 0-59。
- 第二個參數(hour):代表一天中的第幾小時,范圍 0-23。
- 第三個參數(day):代表一個月中的第幾天,范圍 1-31。
- 第四個參數(month):代表一年中第幾個月,范圍 1-12。
- 第五個參數(week):代表星期幾,范圍 0-7 (0及7都是星期天)。
- 第六個參數(command):所要執行的指令。
具體應用的時候,時間定義大概是長下面這個樣子!
# 每5分鐘執行一次命令
*/5 * * * * Command
# 每小時的第5分鐘執行一次命令
5 * * * * Command
# 指定每天下午的 6:30 執行一次命令
30 18 * * * Command
# 指定每月8號的7:30分執行一次命令
30 7 8 * * Command
# 指定每年的6月8日5:30執行一次命令
30 5 8 6 * Command
# 指定每星期日的6:30執行一次命令
30 6 * * 0 Command
2.2、具體示例
以centOS操作系統為例,創建一個定時任務,每分鐘執行某個指定shell腳本,過程如下!
- 首先安裝 crond 相關服務
yum -y install cronie yum-cron
- 編寫一個輸出當前時間到日志的shell腳本
#創建一個test.sh腳本
vim /root/shell/test.sh
#腳本內容如下,將內容輸出到file.log文件
echo `date '+%Y-%m-%d %H:%M:%S'` >> /root/shell/file.log
- 先執行一下腳本,觀察內容是否輸出到file.log文件
sh /root/shell/test.sh
- 查看日志文件內容
cat /root/shell/file.log
如果出現以下內容,說明運行正常!
圖片
- 接著再來創建一個定時任務,每分鐘執行一次test.sh
#編輯定時任務【刪除-添加-修改】
crontab -e
- 在文件末尾加入如下信息
#每分鐘執行一次test.sh腳本
*/1 * * * * sh /root/shell/test.sh
- 如果想查定時任務是否加入,可以通過如下命令
#查看crontab定時任務
crontab -l
圖片
- 最后就是重啟定時任務
##重新載入配置
systemctl reload crond
#重啟服務
systemctl restart crond
- 查看file.log文件實時輸出內容,觀察test.sh腳本是否被執行
tail -f /root/shell/file.log
從結果上看,運行正常!
圖片
- 如果想查看定時任務日志,可通過如下命令進行查看
tail -f /var/log/cron
如果想移除某個定時任務,直接輸入crontab -e命令,并移除對應的腳本,然后刷新配置、重啟服務即可!
如果想深入的了解crontab使用,可以參考這篇文章,里面總結得比較好, 這里就不再多說了。
三、java 自帶的定時器
3.1、Timer
Timer 定時器,由 jdk 提供的java.util.Timer和java.util.TimerTask兩個類組合實現。
其中TimerTask表示某個具體任務,而Timer則是進行調度任務處理。
實現過程也很簡單,示例如下:
import java.util.Timer;
import java.util.TimerTask;
public class TimerTest extends TimerTask {
private String jobName;
public TimerTest(String jobName) {
this.jobName = jobName;
}
@Override
public void run() {
System.out.println("execute " + jobName);
}
public static void main(String[] args) {
Timer timer = new Timer();
long delay1 = 1 * 1000;
long period1 = 1 * 1000;
// 從現在開始 1 秒鐘之后,每隔 1 秒鐘執行一次 job1
timer.schedule(new TimerTest("job1"), delay1, period1);
long delay2 = 2 * 1000;
long period2 = 2 * 1000;
// 從現在開始 2 秒鐘之后,每隔 2 秒鐘執行一次 job2
timer.schedule(new TimerTest("job2"), delay2, period2);
}
}
輸出結果:
execute job1
execute job2
execute job1
execute job1
execute job2
execute job1
execute job1
execute job2
...
Timer 的優點在于簡單易用,由于所有任務都是由同一個線程來調度,因此所有任務都是串行執行的,同一時間只能有一個任務在執行,前一個任務的延遲或異常都將會影響到之后的任務。
具體原因如下:
- 1.當一個線程拋出異常時,整個 Timer 都會停止運行。例如上面的 job1 拋出異常的話,job2 也不會再跑了
- 當一個線程里面處理的時間非常長的時候,會影響其他 job 的調度。例如,如果 job1 處理的時間要 1 分鐘, 那么 job2 至少要等 1 分鐘之后才能跑。
基于上面的原因,Timer 現在生產環境中都不在使用!
3.2、ScheduledExecutor
鑒于 Timer 的上述缺陷,從 Java 5 開始,推出了基于線程池設計的 ScheduledExecutor。
其設計思想是,每一個被調度的任務都會由線程池中一個線程來管理執行,因此任務是并發執行的,相互之間不會受到干擾。需要注意的是,只有當任務的執行時間到來時,ScheduedExecutor 才會真正啟動一個線程,其余時間 ScheduledExecutor 都是在輪詢任務的狀態。
實現過程,示例如下:
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class ScheduledExecutorTest implements Runnable {
private String jobName;
public ScheduledExecutorTest(String jobName) {
this.jobName = jobName;
}
@Override
public void run() {
System.out.println("execute " + jobName);
}
public static void main(String[] args) {
//設置10個核心線程
ScheduledExecutorService service = Executors.newScheduledThreadPool(10);
long initialDelay1 = 1;
long period1 = 1;
// 從現在開始1秒鐘之后,每隔1秒鐘執行一次job1
service.scheduleAtFixedRate(
new ScheduledExecutorTest("job1"), initialDelay1,
period1, TimeUnit.SECONDS);
long initialDelay2 = 2;
long delay2 = 2;
// 從現在開始2秒鐘之后,每隔2秒鐘執行一次job2
service.scheduleWithFixedDelay(
new ScheduledExecutorTest("job2"), initialDelay2,
delay2, TimeUnit.SECONDS);
}
}
輸出結果:
execute job1
execute job1
execute job2
execute job1
execute job1
execute job2
execute job1
在 ScheduledExecutorService 中,由initialDelay、delay和TimeUnit三個參數決定任務的執行頻率。
其中:
- TimeUnit:表示執行的單位,例如:毫秒、秒、分、小時、天...
- initialDelay:表示從現在開始多少TimeUnit后執行任務
- delay:表示任務執行周期,每隔多少TimeUnit執行一次任務
從 api 上可以看到,ScheduledExecutorService 的出現,完全可以替代 Timer ,同時完美的解決上面所說的 Timer 存在的兩個問題!
- 當任務拋異常時,即使異常沒有被捕獲, 線程池也還會新建線程,所以定時任務不會停止
- 由于 ScheduledExecutorService 是不同線程處理不同的任務,因此不管一個線程的運行時間有多長,都不會影響到另外一個線程的運行
但是 ScheduledExecutorService 也不是萬能的,比如我想每月1號統計一次報表、每季度月末統計銷售額等等這樣的需求。
你會發現使用 ScheduledExecutorService 實現的時候,每次任務執行之后,你需要從當前時間開始出下一次執行時間的間隔,而且每次都要重算,非常麻煩!
遇到這樣的需求,就需要一個更加完善的任務調度框架來解決這些復雜的調度問題。
而我們所熟悉的開源框架 Quartz 在這方面就提供了強大的支持。
四、第三方定時器
4.1、Quartz
quartz 在 java 項目中應用非常的廣,市面上很多的開源調度框架也基本都是直接或間接基于這個框架來開發的。
下面我們就通過一個例子,來簡單地認識一下它。
- 引入quartz包
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.2</version>
</dependency>
- 編寫示例代碼
public class QuartzTest implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
}
public static void main(String[] args) throws SchedulerException {
// 創建一個Scheduler
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
// 啟動Scheduler
scheduler.start();
// 新建一個Job, 指定執行類是QuartzTest, 指定一個K/V類型的數據, 指定job的name和group
JobDetail job = JobBuilder.newJob(QuartzTest.class)
.usingJobData("jobData", "test")
.withIdentity("myJob", "myJobGroup")
.build();
// 新建一個Trigger, 表示JobDetail的調度計劃, 這里的cron表達式是 每1秒執行一次
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("myTrigger", "myTriggerGroup")
.startNow()
.withSchedule(CronScheduleBuilder.cronSchedule("0/5 * * * * ?"))
.build();
// 讓scheduler開始調度這個job, 按trigger指定的計劃
scheduler.scheduleJob(job, trigger);
}
}
輸出結果:
2020-11-09 21:38:40
2020-11-09 21:38:45
2020-11-09 21:38:50
2020-11-09 21:38:55
2020-11-09 21:39:00
2020-11-09 21:39:05
2020-11-09 21:39:10
...
當然,你還可以從JobExecutionContext對象中,獲取上文usingJobData()方法中設置的值。
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
//從context中獲取instName,groupName以及dataMap
String jobName = context.getJobDetail().getKey().getName();
String groupName = context.getJobDetail().getKey().getGroup();
JobDataMap dataMap = context.getJobDetail().getJobDataMap();
//從dataMap中獲取myDescription,myValue以及myArray
String value = dataMap.getString("jobData");
System.out.println("jobName:" + jobName + ",groupName:" + groupName + ",jobData:" + value);
}
輸出結果:
jobName:myJob,groupName:myJobGroup,jobData:test
在 Quartz 工具包中,設計的核心類主要包括 Scheduler, Job 以及 Trigger。
- Scheduler:可以理解為是一個具體的調度實例,用來調度任務
- JobDetail:定義具體作業的實例,進一步封裝和拓展了 Job 功能,其中 Job 是一個接口,類似上面的 TimerTask
- Trigger:設置任務調度策略。例如多久執行一次,什么時候執行,以什么頻率執行等等
相比 JDK 提供的任務調度服務,Quartz 最明顯的一個特點就是將任務調度者、任務具體實例、任務調度策略進行三方解耦,這么做的優點在于同一個 Job 可以綁定多個不同的 Trigger,同一個 Trigger 也可以調度多個 Job,配置靈活性非常強。
Trigger 同時還支持cron表達式,在任務調度時間配置方面,更加靈活。
當然,Quartz 的用途不僅僅在單例服務上,在分布式調度方面也同樣應用非常廣,由于篇幅原因,關于 Quartz 的詳細使用介紹,我們會在后期的文章中詳細深入分析。
4.2、Spring Schedule
與 Quartz 齊名的還有我們所熟悉的 Spring Schedule,由 Spring 原生提供支持。
實現上,Spring 中使用定時任務也非常簡單。
4.2.1、基于 XML 配置
- 在springApplication.xml配置文件中加入如下信息
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:task="http://www.springframework.org/schema/task"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.0.xsd">
<!-- 定時器開關-->
<task:annotation-driven />
<!-- 將TestTask注入ioc-->
<bean id="myTask" class="com.spring.task.TestTask"></bean>
<!-- 定時調度方法配置-->
<task:scheduled-tasks>
<!-- 這里表示的是每隔5秒執行一次 -->
<task:scheduled ref="myTask" method="show" cron="*/5 * * * * ?" />
<!-- 這里表示的是每隔10秒執行一次 -->
<task:scheduled ref="myTask" method="print" cron="*/10 * * * * ?"/>
</task:scheduled-tasks>
<!-- 自動掃描的包名 -->
<context:component-scan base-package="com.spring.task" />
</beans>
- 定義自己的任務執行邏輯
package com.spring.task;
/**
* 定義任務
*/
public class TestTask {
public void show() {
System.out.println("show method 1");
}
public void print() {
System.out.println("print method 1");
}
}
4.2.2、基于注解配置
基于注解的配置,可以直接在方法上配置相應的調度策略,相比xml的方式更加簡潔。
- 實現過程如下
package com.spring.task;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
/**
* 基于注解的定時器
*/
@Component
public class TestTask2 {
/**
* 定時計算。每隔5秒執行一次
*/
@Scheduled(cron = "*/5 * * * * ?")
public void show() {
System.out.println("show method 2");
}
/**
* 啟動1分鐘后執行,之后每隔10秒執行一次
*/
@Scheduled(initialDelay = 60*1000, fixedRate = 10*1000)
public void print() {
System.out.println("print method 2");
}
}
4.2.3、Spring Boot 定時任務應用
如果在 Spring Boot 項目中,使用就更加方便了。
- 首先在程序入口啟動類添加@EnableScheduling,開啟定時任務功能
@SpringBootApplication
@EnableScheduling
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
- 編寫定時任務邏輯
@Component
public class TestTask3 {
private int count=0;
@Scheduled(crnotallow="*/5 * * * * ?")
private void process() {
System.out.println("this is scheduler task running "+(count++));
}
}
輸出結果:
this is scheduler task running 0
this is scheduler task running 1
this is scheduler task running 2
...
4.2.4、任務執行規則
最后,我們來看看 Spring Schedule 的任務執行規則,打開@Scheduled注解類,源碼如下:
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(Schedules.class)
public @interface Scheduled {
String cron() default "";
String zone() default "";
long fixedDelay() default -1;
String fixedDelayString() default "";
long fixedRate() default -1;
String fixedRateString() default "";
long initialDelay() default -1;
String initialDelayString() default "";
}
從方法上可以看出,@Scheduled注解中可以傳 8 種參數:
- cron:指定 cron 表達式
- zone:默認使用服務器默認時區。可以設置為java.util.TimeZone中的zoneId
- fixedDelay:從上一個任務完成開始到下一個任務開始的間隔,單位毫秒
- fixedDelayString:同上,時間值是String類型
- fixedRate:從上一個任務開始到下一個任務開始的間隔,單位毫秒
- fixedRateString:同上,時間值是String類型
- initialDelay:任務首次執行延遲的時間,單位毫秒
- initialDelayString:同上,時間值是String類型
其中用的最多的就是cron表達式,下面我們就一起來看看如何來編寫配置。
4.2.5、Cron 表達式
Spring 的 cron 表達式和 Quartz 的 cron 表達式基本都是通用的,但是與 linux 下 crontab 的 cron 表達式是有一定區別的,它可以直接到秒級別。
cron 表達式結構:
.---------------------- seconds(0 - 59)
| .------------------- minute (0 - 59)
| | .---------------- hour (0 - 23)
| | | .------------- day of month (1 - 31)
| | | | .---------- month (1 - 12)
| | | | | .------- Day-of-Week (1 - 7)
| | | | | | .---- year (1970 - 2099) ...
| | | | | | |
* * * * * ? *
具體樣例如下:
圖片
在 cron 表達式中不區分大小寫,更多配置可以參考這里。
還可以在線解析cron表達式進行測試。
圖片
4.3、elastic-job
elastic-job 是當當基于 quartz 二次開發而開源的一個分布式彈性作業框架,功能十分強大。
主要功能:
- 分布式:重寫 Quartz 基于數據庫的分布式功能,改用 Zookeeper 實現注冊中心。
- 并行調度:采用任務分片方式實現。將一個任務拆分為n個獨立的任務項,由分布式的服務器并行執行各自分配到的分片項。
- 彈性擴容縮容:將任務拆分為 n 個任務項后,各個服務器分別執行各自分配到的任務項。一旦有新的服務器加入集群,或現有服務器下線,elastic-job將在保留本次任務執行不變的情況下,下次任務開始前觸發任務重分片。
- 集中管理:采用基于Zookeeper的注冊中心,集中管理和協調分布式作業的狀態,分配和監聽。外部系統可直接根據Zookeeper的數據管理和監控elastic-job。
- 定制化流程型任務:作業可分為簡單和數據流處理兩種模式,數據流又分為高吞吐處理模式和順序性處理模式,其中高吞吐處理模式可以開啟足夠多的線程快速的處理數據,而順序性處理模式將每個分片項分配到一個獨立線程,用于保證同一分片的順序性,這點類似于kafka的分區順序性。
由于 elastic-job 是基于 Zookeeper 實現集群調度,因此在使用它之前,需要先搭建好 Zookeeper 服務器,網上教程很多,在此不過多介紹。
elastic-job 具體簡單實現過程如下!
- 引入相關 elastic-job 依賴
<dependencies>
<!-- 引入elastic-job-lite核心模塊 -->
<dependency>
<groupId>com.dangdang</groupId>
<artifactId>elastic-job-lite-core</artifactId>
<version>2.0.5</version>
</dependency>
<!-- 使用springframework自定義命名空間時引入 -->
<dependency>
<groupId>com.dangdang</groupId>
<artifactId>elastic-job-lite-spring</artifactId>
<version>2.0.5</version>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.9</version>
</dependency>
</dependencies>
- 定義job
public class MyElasticJob implements SimpleJob {
public void execute(ShardingContext context) {
System.out.println(context.toString());
switch (context.getShardingItem()) {
case 0:
System.out.println("------------->>>>0");
break;
case 1:
System.out.println("------------->>>>1");
break;
case 2:
System.out.println("------------->>>>2");
break;
default:
System.out.println("------------->>>>default");
break;
}
}
public static void main(String[] args) {
new JobScheduler(createRegistryCenter(), createJobConfiguration()).init();
}
private static CoordinatorRegistryCenter createRegistryCenter() {
//配置ZK注冊中心地址
CoordinatorRegistryCenter regCenter = new ZookeeperRegistryCenter(new ZookeeperConfiguration("10.211.108.160:2181", "elastic-job-demo"));
regCenter.init();
return regCenter;
}
private static LiteJobConfiguration createJobConfiguration() {
JobCoreConfiguration simpleCoreConfig = JobCoreConfiguration.newBuilder("demoSimpleJob", "0/15 * * * * ?", 10).build();
// 定義SIMPLE類型配置
SimpleJobConfiguration simpleJobConfig = new SimpleJobConfiguration(simpleCoreConfig, MyElasticJob.class.getCanonicalName());
// 定義Lite作業根配置
LiteJobConfiguration simpleJobRootConfig = LiteJobConfiguration.newBuilder(simpleJobConfig).build();
return simpleJobRootConfig;
}
}
具體詳細使用,可以參考官方網站。
4.4、xxl-job
xxl-job 是被廣泛使用的另外一款使用的分布式任務調度框架,出自大眾點評許雪里(xxl 就是作者名字的拼音首字母)的開源項目,官網上介紹這是一個輕量級分布式任務調度框架,其核心設計目標是開發迅速、學習簡單、輕量級、易擴展。
圖片
跟elasticjob不同,xxl-job 環境依賴于mysql,不用 ZooKeeper,這也是最大的不同。早起的 xxljob 也是基于 quartz 開發的,不過現在慢慢去 quartz 化了,改成自研的調度模塊。
xxl-job 具體簡單實現過程如下!
- 引入相關 xxl-job 依賴
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>
<version>2.2.0</version>
</dependency>
</dependencies>
- 在application.properties添加相關配置
# web port
server.port=8081
# log config
logging.cnotallow=classpath:logback.xml
spring.application.name=xxljob-demo
### 調度中心部署跟地址 [選填]:如調度中心集群部署存在多個地址則用逗號分隔。執行器將會使用該地址進行"執行器心跳注冊"和"任務結果回調";為空則關閉自動注冊;
xxl.job.admin.addresses=http://127.0.0.1:8080/xxl-job-admin
### 執行器通訊TOKEN [選填]:非空時啟用;
xxl.job.accessToken=
### 執行器AppName [選填]:執行器心跳注冊分組依據;為空則關閉自動注冊
xxl.job.executor.appname=xxl-job-demo
### 執行器注冊 [選填]:優先使用該配置作為注冊地址,為空時使用內嵌服務 ”IP:PORT“ 作為注冊地址。從而更靈活的支持容器類型執行器動態IP和動態映射端口問題。
xxl.job.executor.address=
### 執行器IP [選填]:默認為空表示自動獲取IP,多網卡時可手動設置指定IP,該IP不會綁定Host僅作為通訊實用;地址信息用于 "執行器注冊" 和 "調度中心請求并觸發任務";
xxl.job.executor.ip=
### 執行器端口號 [選填]:小于等于0則自動獲取;默認端口為9999,單機部署多個執行器時,注意要配置不同執行器端口;
xxl.job.executor.port=9999
### 執行器運行日志文件存儲磁盤路徑 [選填] :需要對該路徑擁有讀寫權限;為空則使用默認路徑;
xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler
### 執行器日志文件保存天數 [選填] : 過期日志自動清理, 限制值大于等于3時生效; 否則, 如-1, 關閉自動清理功能;
xxl.job.executor.logretentinotallow=10
- 編寫一個配置類XxlJobConfig
@Configuration
public class XxlJobConfig {
private Logger logger = LoggerFactory.getLogger(XxlJobConfig.class);
@Value("${xxl.job.admin.addresses}")
private String adminAddresses;
@Value("${xxl.job.accessToken}")
private String accessToken;
@Value("${xxl.job.executor.appname}")
private String appname;
@Value("${xxl.job.executor.address}")
private String address;
@Value("${xxl.job.executor.ip}")
private String ip;
@Value("${xxl.job.executor.port}")
private int port;
@Value("${xxl.job.executor.logpath}")
private String logPath;
@Value("${xxl.job.executor.logretentiondays}")
private int logRetentionDays;
@Bean
public XxlJobSpringExecutor xxlJobExecutor() {
logger.info(">>>>>>>>>>> xxl-job config init.");
XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
xxlJobSpringExecutor.setAdminAddresses(adminAddresses);
xxlJobSpringExecutor.setAppname(appname);
xxlJobSpringExecutor.setAddress(address);
xxlJobSpringExecutor.setIp(ip);
xxlJobSpringExecutor.setPort(port);
xxlJobSpringExecutor.setAccessToken(accessToken);
xxlJobSpringExecutor.setLogPath(logPath);
xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays);
return xxlJobSpringExecutor;
}
}
- 編寫具體任務類XxlJobDemoHandler
@Component
public class XxlJobDemoHandler {
/**
* Bean模式,一個方法為一個任務
* 1、在Spring Bean實例中,開發Job方法,方式格式要求為 "public ReturnT<String> execute(String param)"
* 2、為Job方法添加注解 "@XxlJob(value="自定義jobhandler名稱", init = "JobHandler初始化方法", destroy = "JobHandler銷毀方法")",注解value值對應的是調度中心新建任務的JobHandler屬性的值。
* 3、執行日志:需要通過 "XxlJobLogger.log" 打印執行日志;
*/
@XxlJob("demoJobHandler")
public ReturnT<String> demoJobHandler(String param) throws Exception {
XxlJobLogger.log("java, Hello World~~~");
XxlJobLogger.log("param:" + param);
return ReturnT.SUCCESS;
}
}
寫完之后啟動服務,然后可以打開管理界面,找到執行器管理,添加執行器。
圖片
接著到任務管理,添加任務。
最后我們可以到任務管理去測試一下,運行demoJobHandler。
圖片
圖片
點擊保存后,會立即執行。點擊查看日志,可以看到任務執行的歷史日志記錄
圖片
這就是簡單的Demo演示,具體詳細使用,可以參考官方網站。
五、總結
本文主要圍繞定時調度器的理論知識和用法做了一次知識的總結,如果有理解不對的地方,歡迎大家留言指出。
六、參考
1、https://juejin.im/post/6844904002606350343
2、https://developer.ibm.com/zh/languages/java/articles/j-lo-taskschedule/
3、https://www.cnblogs.com/linjiqin/p/11720673.html
4、https://www.cnkirito.moe/timer/
5、https://cloud.tencent.com/developer/article/1138669
6、https://developer.aliyun.com/article/775305