Spring Scheduler定時任務 + Quartz


原文地址: https://blog.csdn.net/revitalizing/article/details/61420556

 

版權聲明:本文為博主原創文章,未經博主允許不得轉載。 https://blog.csdn.net/lzx_2011/article/details/61420556

定時任務幾種實現方式

  1. Java自帶的java.util.Timer類,這個類允許你調度一個java.util.TimerTask任務,沒怎么用過就不說了。
  2. Spring3.0以后自帶的task,可以將它看成一個輕量級的Quartz,而且使用起來比Quartz簡單許多。
  3. java的線程池類ScheduledExecutorService也可以實現一些簡單的定時任務,周期性任務。
  4. Quartz是一個功能比較強大的的調度器,可以讓你的程序在指定時間執行,也可以按照某一個頻度執行,可以方便的分布式部署、便捷的監控和管理任務,適合任務很多的情況。

Spring Scheduler注解方式實現

代碼還是挺少的

@Component public class SchedulerPractice{ // @Scheduled(fixedDelay=60000) @Scheduled(cron = "0 0/1 * * * ?")
 public void execute() {
 logger.info("every one minute------");
 } }
  • 1
  • 2
  • 3
  • 4
  • 5

配置文件

<beans xmlns="http://www.springframework.org/schema/beans" 
 xmlns:task="http://www.springframework.org/schema/task" 
 http://www.springframework.org/schema/task 
 http://www.springframework.org/schema/task/spring-task-3.0.xsd
 > <context:annotation-config /> <!—spring掃描注解的配置 --> <context:component-scan base-package="com.liu” /> <task:annotation-driven />
  • 1
  • 2
  • 3
  • 4
  • 5

注解@Scheduled 中有三個方法,用來對執行規則的配置:

cron:指定cron表達式,文章最后有些配置示例。

fixedDelay:即表示從上一個任務完成開始到下一個任務開始的間隔,單位是毫秒。

fixedRate:即從上一個任務開始到下一個任務開始的間隔,單位是毫秒。

執行時間可配置化

將cron表達式配置在java的properties文件或者環境變量中,是配置更靈活

xml配置方式


<task:scheduler id="myScheduler"/> <task:scheduled-tasks scheduler="myScheduler" pool-size="2" /> <task:scheduled ref=“testScheduler” method="execute()” cron="${cron_expression}"/> </task:scheduled-tasks> 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

pool-size=”2” 有多個任務可以配置以線程池執行

注解使用方式

@Scheduled(cron = "${cron_expression}")
  • 1

分布式多實例運行

scheduler與web配置在一起,在高可用的情況下,如果有多個web容器實例,scheduler會在多個實例上同時運行。

解決辦法:

  1. 使用寫死服務器Host的方式執行task,存在單點風險,負載均衡手動完成。(或者一台代碼中配置任務,其他不配置任務)

  2. 在task的基類加入一些邏輯,當開始運行時,將狀態(運行機器的IP、時間等)寫入數據庫、緩存(redis)或者zk,運行結束時重置狀態。其它實例看到有這樣的數據,就直接返回。帶來的問題是: 
    一定要保證結束運行后將狀態重置,否則下一個運行周期,所有的task都會返回的。 
    因為讀寫狀態並非原子操作,偶爾也會發生task同時運行的事。

  3. 使用zk分布式鎖,比如在任務執行方法上自定義注解,在注解中配置鎖在zk的路徑,在該注解上自定義個攔截器,在攔截器中獲取zk鎖。

Java線程池ScheduledExecutorService

示例代碼如下

public void cronThread(){ ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(1); scheduledThreadPool.scheduleWithFixedDelay(new ThreadPractice(), 0, 3, TimeUnit.SECONDS); }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

new ThreadPractice()是一個實現了Runnable的類。 
ScheduledExecutorService 類有兩個方法

public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

這兩個方法和@scheduled 注解中的fixedDelay和fixedRate類似。

Quartz

Quartz框架是一個全功能、開源的任務調度服務,可以集成幾乎任何的java應用程序—從小的單片機系統到大型的電子商務系統。可以方便的分布式部署、便捷的監控和管理任務,Quartz可以執行上千上萬的任務調度。

核心概念

Quartz核心的概念:scheduler任務調度、Job任務、Trigger觸發器、JobDetail任務細節

  • Job任務:其實Job是接口,其中只有一個execute方法, 只要實現此接口,實現execute方法即可。

  • JobDetail:任務細節,Quartz執行Job時,需要新建個Job實例,但是不能直接操作Job類,所以通過JobDetail來獲取Job的名稱、描述信息。

  • Trigger觸發器:執行任務的規則;比如每天,每小時等。觸發器有SimpleTrigger和CronTrigger,這個觸發器實現了Trigger接口。對於復雜的時間表達式來說,比如每個月15日上午幾點幾分,使用CronTrigger,對於簡單的時間來說,比如每天執行幾次,使用SimpleTrigger。

  • scheduler任務調度:是最核心的概念,需要把JobDetail和Trigger注冊到scheduler中,才可以執行。

quartz單機示例

使用的quartz的jar的版本是:2.2.1 ,低版本的核心類可能有些不同。

job類

public class MyJob implements Job { @Override //把要執行的操作,寫在execute方法中 public void execute(JobExecutionContext arg0) throws JobExecutionException { System.out.println(“test Quartz"+new Date()); } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

測試代碼

@Test public void startJobTest() { SchedulerFactory schedulerfactory = new StdSchedulerFactory(); Scheduler scheduler = null; try { // 通過schedulerFactory獲取一個調度器 scheduler = schedulerfactory.getScheduler(); // 創建jobDetail實例,綁定Job實現類 // 指明job的名稱,所在組的名稱,以及綁定job類 JobDetail job = JobBuilder.newJob(ImageTableMonitorJob.class).withIdentity("job1", "jgroup1").build(); // 定義調度觸發規則 // 使用simpleTrigger規則 Trigger trigger = TriggerBuilder.newTrigger().withIdentity("simpleTrigger", "triggerGroup") .withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(1).withRepeatCount(8)).startNow().build(); // 使用cornTrigger規則 每天10點42分 // Trigger trigger= TriggerBuilder.newTrigger().withIdentity("simpleTrigger", "triggerGroup") // .withSchedule(CronScheduleBuilder.cronSchedule("0 42 10 * * ? *")) // .startNow().build(); // 把作業和觸發器注冊到任務調度中 scheduler.scheduleJob(job, trigger); // 啟動調度 scheduler.start(); } catch (Exception e) { // e.printStackTrace(); } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

spring提供了對quartz的整合,可以通過 org.springframework.scheduling.quartz.SchedulerFactoryBean 注入scheduler調度器,並且對調度器做些配置,比如使用線程池,並配置線程數量等。配置示例如下。

<bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean" > <property name="taskExecutor" ref="taskExecutor" /> <property name="autoStartup" value="false"/> </bean> <bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor"> <property name="corePoolSize" value="2" /> <property name="maxPoolSize" value="512" /> </bean>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

Quartz分布式原理

Quartz的集群部署方案在架構上是分布式的,沒有負責集中管理的節點,而是利用數據庫鎖的方式來實現集群環境下進行並發控制。分布式部署時需要保證各個節點的系統時間一致。沒弄過,就不細說了。

Quartz數據庫核心表QRTZ_LOCKS中有5條記錄CALENDAR_ACCESS,JOB_ACCESS,MISFIRE_ACCESS,STATE_ACCESS,TRIGGER_ACCESS 代表5把鎖,分別用於實現多個Quartz Node對Job、Trigger、Calendar訪問的同步控制。

cron表達式

字段   允許值   允許的特殊字符
秒    0-59 , - * / 分 0-59 , - * / 小時 0-23 , - * / 日期 1-31 , - * ? / L W C 月份 1-12 或者 JAN-DEC , - * / 星期 1-7 或者 SUN-SAT , - * ? / L C # 年(可選) 留空, 1970-2099 , - * / - 區間 * 通配符 ? 你不想設置那個字段 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

下面列出一些實例

CRON表達式    含義 
"0 0 12 * * ?" 每天中午十二點觸發 "0 15 10 ? * *" 每天早上10:15觸發 "0 15 10 * * ?" 每天早上10:15觸發 "0 15 10 * * ? *" 每天早上10:15觸發 "0 15 10 * * ? 2005" 2005年的每天早上10:15觸發 "0 * 14 * * ?" 每天從下午2點開始到2點59分每分鍾一次觸發 "0 0/5 14 * * ?" 每天從下午2點開始到2:55分結束每5分鍾一次觸發 "0 0/5 14,18 * * ?" 每天的下午2點至2:55和6點至6點55分兩個時間段內每5分鍾一次觸發 "0 0-5 14 * * ?" 每天14:00至14:05每分鍾一次觸發 "0 10,44 14 ? 3 WED" 三月的每周三的14:10和14:44觸發 "0 15 10 ? * MON-FRI" 每個周一、周二、周三、周四、周五的10:15觸發 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

參考資料

Java任務調度框架Quartz教程實例 
Spring Scheduler的使用與坑 
Quartz應用與集群原理分析


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM