Quartz開源調度框架原理及使用Quartz實現超時訂單測試

Quartz

Quartz 是 OpenSymphony 開源組織在 Job scheduling 領域又一個開源項目,它可以與 J2EE 與 J2SE 應用程序相結合也可以單獨使用。Quartz 可以用來創建簡單或爲運行十個,百個,甚至是好幾萬個 Jobs 這樣複雜的程序。Jobs 可以做成標準的 Java 組件或 EJBs。Quartz 的最新版本爲 Quartz 2.3.0。

1.1 Quartz 調用流程

在這裏插入圖片描述

  • JOB: 定義自己的任務
  • JobDetail: 封裝 JOB 對象的
  • 調度器: 管理全部的任務(Scheduler)
  • 觸發器: 開啓新的線程執行任務(jobDetail)

1.2 Quartz組件說明

1.2.1 Scheduler – 調度器

     Scheduler 被用來對 Trigger 和 Job 進行管理。Trigger 和 JobDetail 可以註冊到Scheduler 中,兩者在 Scheduler 中都擁有自己的唯一的組和名稱用來進行彼此的區分,Scheduler 可以通過組名或者名稱來對 Trigger 和 JobDetail 來進行管理。一個 Trigger只能對應一個 Job,但是一個 Job 可以對應多個 Trigger。每個 Scheduler 都包含一個SchedulerContext,用來保存 Scheduler 的上下文。Job 和 Trigger 都可以獲取SchedulerContext 中的信息。

     Scheduler 包含兩個重要的組件,JobStore 和 ThreadPool。JobStore 用來存儲運行時信息,包括 Trigger,Schduler,JobDetail,業務鎖等。它有多種實現 RAMJob(內存實現),JobStoreTX(JDBC,事務由 Quartz 管理)等。ThreadPool 就是線程池,Quartz有自己的線程池實現。所有任務的都會由線程池執行。

     Scheduler 是由 SchdulerFactory 創建,它有兩個實現:DirectSchedulerFactory和 StdSchdulerFactory。前者可以用來在代碼裏定製你自己的 Schduler 參數。後者是直接讀取 classpath 下的 quartz.properties(不存在就都使用默認值)配置來實例化Schduler。通常來講,我們使用 StdSchdulerFactory 也就足夠了。

1.2.2 Trigger – 觸發器

     Trigger 是用來定義 Job 的執行規則,主要有四種觸發器,其中 SimpleTrigger 和CronTrigger 觸發器用的最多。
     SimpleTrigger:從某一個時間開始,以一定的時間間隔來執行任務。它主要有兩個屬性,repeatInterval 重複的時間間隔;repeatCount 重複的次數,實際上執行的次數是 n+1,因爲在 startTime 的時候會執行一次。
     CronTrigger:適合於複雜的任務,使用 cron 表達式來定義執行規則。
     CalendarIntervalTrigger:類似於 SimpleTrigger,指定從某一個時間開始,以一定的時間間隔執行的任務。 但是 CalendarIntervalTrigger 執行任務的時間間隔比SimpleTrigger 要豐富,它支持的間隔單位有秒,分鐘,小時,天,月,年,星期。相較於 SimpleTrigger 有兩個優勢:1、更方便,比如每隔 1 小時執行,你不用自己去計算 1小時等於多少毫秒。 2、支持不是固定長度的間隔,比如間隔爲月和年。但劣勢是精度只能到秒。它的主要兩個屬性,interval執行間隔intervalUnit 執行間隔的單位(秒,分鐘,小時,天,月,年,星期)。
     DailyTimeIntervalTrigger:指定每天的某個時間段內,以一定的時間間隔執行任務。並且它可以支持指定星期。它適合的任務類似於:指定每天 9:00 至 18:00 ,每隔70 秒執行一次,並且只要週一至週五執行。它的屬性有 startTimeOfDay 每天開始時間;endTimeOfDay 每天結束時間;daysOfWeek 需要執行的星期;interval 執行間隔;intervalUnit 執行間隔的單位(秒,分鐘,小時,天,月,年,星期);repeatCount 重複次數
     所有的 trigger 都包含了 StartTime 和 endTIme 這兩個屬性,用來指定 Trigger被觸發的時間區間。
     所有的 trigger 都可以設置 MisFire 策略,該策略是對於由於系統奔潰或者任務時間過長等因導致trigger在應該觸發的時間點沒有觸發,並且超過了misfireThreshold設置的時間(默認是一分鐘,沒有超過就立即執行)就算 misfire 了,這個時候就該設置如何應對這種變化了。激活失敗指令(Misfire Instructions)是觸發器的一個重要屬性,它指定了 misfire 發生時調度器應當如何處理。所有類型的觸發器都有一個默認的指令,叫做Trigger.MISFIRE_INSTRUCTION_SMART_POLICY,但是這個這個“聰明策略”對於不同類型的觸發器其具體行爲是不同的。對於 SimpleTrigger,這個“聰明策略”將根據觸發器實例的狀態和配置來決定其行。

1.2.3 SimpleTrigger 常見策略:

  • MISFIRE_INSTRUCTION_FIRE_NOW 立刻執行。對於不會重複執行的任務,這是默認的處理策略。
  • MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT 在下一個激活點執行,且超時期內錯過的執行機會作廢。
  • MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_COUNT 立即執行,且超時期內錯過的執行機會作廢。
  • MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT 在下一個激活點執行,並重復到指定的次數。
  • MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_COUNT 立即執行,並重復到指定的次數。
  • MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY 忽略所有的超時狀態,按照觸發器的策略執行。

對於 CronTrigger,該“聰明策略”默認選擇 MISFIRE_INSTRUCTION_FIRE_ONCE_NOW以指導其行爲。

1.2.4 CronTrigger 常見策略:

  • MISFIRE_INSTRUCTION_FIRE_ONCE_NOW 立刻執行一次,然後就按照正常的計劃執行。
  • MISFIRE_INSTRUCTION_DO_NOTHING 目前不執行,然後就按照正常的計劃執行。這意味着如果下次執行時間超過了 end time,實際上就沒有執行機會了。

1.2.5 Job

     Job 是 一 個 任 務 接 口 , 開 發 者 定 義 自 己 的 任 務 須 實 現 該 接 口 實 現 void execute(JobExecutionContext context)方法,JobExecutionContext 中提供了調度上下文的各種信息。Job 中的任務有可能併發執行,例如任務的執行時間過長,而每次觸發的時間間隔太短,則會導致任務會被併發執行。如果是併發執行,就需要一個數據庫鎖去避免一個數據被多次處理。可以 execute ()方法上添加註解@DisallowConcurrentExecution 解決這個問題。

1.2.6 JobDetail

     Quartz 在每次執行 Job 時,都重新創建一個 Job 實例,所以它不直接接受一個 Job的實例,相反它接收一個 Job 實現類,以便運行時通過 newInstance()的反射機制實例化Job。因此需要通過一個類來描述 Job 的實現類及其它相關的靜態信息,如 Job 名字、描述、關聯監聽器等信息,JobDetail 承擔了這一角色。所以說 JobDetail 是任務的定義,而 Job是任務的執行邏輯。

1.2.7 Calendar

     Calendar:org.quartz.Calendar 和 java.util.Calendar 不同,它是一些日曆特定時間點的集合(可以簡單地將 org.quartz.Calendar 看作 java.util.Calendar 的集合——java.util.Calendar 代表一個日曆時間點,無特殊說明後面的 Calendar 即指org.quartz.Calendar)。一個 Trigger 可以和多個 Calendar 關聯,以便排除或包含某些時間點。

1.3 SpringBoot整合Quartz – 實現訂單超時任務

業務需求:

  • 說明:當訂單創建之後,如果長時間不支付.則訂單會超時.
  • 條件: status=1 未支付. status=6 交易關閉.
  • 規定: 30 分鐘超時.

1.3.1 導入jar包

<!--添加 Quartz 的支持 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-quartz</artifactId>
</dependency>

1.3.2 編輯配置類

@Configuration	//標識配置類
public class OrderQuartzConfig {
	
	//定義任務詳情
	@Bean
	public JobDetail orderjobDetail() {
		//指定job的名稱和持久化保存任務
		return JobBuilder
				.newJob(OrderQuartz.class)	//引入自己的job
				.withIdentity("orderQuartz")  //定義任務名稱
				.storeDurably()
				.build();
	}
	//定義觸發器
	@Bean
	public Trigger orderTrigger() {
		/*SimpleScheduleBuilder builder = SimpleScheduleBuilder.simpleSchedule()
				.withIntervalInMinutes(1)	//定義時間週期
				.repeatForever();*/
		//定義調度器
		//"0 0/1 * * * ?"   表示每隔1分鐘執行一次
		CronScheduleBuilder scheduleBuilder 
			= CronScheduleBuilder.cronSchedule("0 0/1 * * * ?");
		return TriggerBuilder
				.newTrigger()
				.forJob(orderjobDetail())
				.withIdentity("orderQuartz")
				.withSchedule(scheduleBuilder).build();
	}
}

1.3.3 定義任務 – OrderQuartz

//準備訂單定時任務
@Component
public class OrderQuartz extends QuartzJobBean{
	
	//修改數據庫的超時訂單的
	@Autowired
	private OrderMapper orderMapper;
	
	/**
	 * 條件:30分鐘超時    1改爲6
	 *判斷依據:    創建訂單的時間    now-created>30分鐘
	 *		    created<now-30
	 *sql: update tb_order set status=6,updated=#{date}
	 *		where created <#{timeOut} and status=1;
	 */
	@Override
	@Transactional
	protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
		//java中專門操作時間的api
		Calendar calendar = Calendar.getInstance(); //獲取當前時間
		//field操作的時間屬性 分鐘 小時  年 月等
		calendar.add(Calendar.MINUTE, -30);
		//獲取計算之後的時間
		Date timeOut = calendar.getTime();
		
		/**
		* entity: 要修改的數據 挑選其中不爲 null 的元素當 set 條件
		* updateWrapper: 條件構造器
		*/
		Order order = new Order();
		order.setStatus(6)
		     .setUpdated(new Date());
		UpdateWrapper<Order> updateWrapper = new UpdateWrapper<>();
		updateWrapper.eq("status", 1)
					 .lt("created", timeOut);
		orderMapper.update(order, updateWrapper);
		System.out.println("定時任務完成!!!!!");
	}
	
	
	
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章