上一篇文章Spring整合Quartz分佈式調度介紹了Quartz經過數據庫的方式來實現分佈式調度,經過使用數據庫來存儲trigger,job等信息,能夠在停服重啓的時候從新加載上次trigger的狀態,保證了完整性;另外一方面經過數據庫來實現鎖機制來實現分佈式調度;Quartz默認提供了11張表,本文將對這幾張表作簡要的分析。mysql
1.qrtz_blob_triggers 2.qrtz_cron_triggers 3.qrtz_simple_triggers 4.qrtz_simprop_triggers 5.qrtz_fired_triggers 6.qrtz_triggers 7.qrtz_job_details 8.qrtz_calendars 9.qrtz_paused_trigger_grps 10.qrtz_scheduler_state 11.qrtz_locks
共11張表,前6張都是關於各類triggers的信息,後面包括job,悲觀鎖,調度狀態等信息;相關表操做在類StdJDBCDelegate中,相關sql語句在StdJDBCConstants中;spring
自定義的triggers使用blog類型進行存儲,非自定義的triggers不會存放在此表中,Quartz提供的triggers包括:CronTrigger,CalendarIntervalTrigger,
DailyTimeIntervalTrigger以及SimpleTrigger,這幾個trigger信息會保存在後面的幾張表中;sql
存儲CronTrigger,這也是咱們使用最多的觸發器,在配置文件中作以下配置,便可在qrtz_cron_triggers生成記錄:數據庫
<bean id="firstCronTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean"> <property name="jobDetail" ref="firstTask" /> <property name="cronExpression" value="0/6 * * ? * *" /> <property name="group" value="firstCronGroup"></property> </bean> <bean id="firstTask" class="org.springframework.scheduling.quartz.JobDetailFactoryBean"> <property name="jobClass" value="zh.maven.SQuartz.task.FirstTask" /> <property name="jobDataMap"> <map> <entry key="firstService" value-ref="firstService" /> </map> </property> </bean> <bean id="firstService" class="zh.maven.SQuartz.service.FirstService"></bean>
表達式指定了每隔6秒執行一次,而後指定了要執行的task,task指定了要執行的業務,運行以後能夠查看數據表:segmentfault
mysql> select * from qrtz_cron_triggers; +-------------+------------------+----------------+-----------------+---------------+ | SCHED_NAME | TRIGGER_NAME | TRIGGER_GROUP | CRON_EXPRESSION | TIME_ZONE_ID | +-------------+------------------+----------------+-----------------+---------------+ | myScheduler | firstCronTrigger | firstCronGroup | 0/6 * * ? * * | Asia/Shanghai | +-------------+------------------+----------------+-----------------+---------------+
myScheduler是在定義SchedulerFactoryBean時指定的名稱,其餘字段均可以在上面的配置中找到;maven
存儲SimpleTrigger,在配置文件中作以下配置,便可在qrtz_simple_triggers生成記錄:分佈式
<bean id="firstSimpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean"> <property name="jobDetail" ref="firstSimpleTask" /> <property name="startDelay" value="1000" /> <property name="repeatInterval" value="2000" /> <property name="repeatCount" value="5"></property> <property name="group" value="firstSimpleGroup"></property> </bean> <bean id="firstSimpleTask" class="org.springframework.scheduling.quartz.JobDetailFactoryBean"> <property name="jobClass" value="zh.maven.SQuartz.task.SimpleFirstTask" /> <property name="jobDataMap"> <map> <entry key="firstService" value-ref="simpleFirstService" /> </map> </property> </bean> <bean id="simpleFirstService" class="zh.maven.SQuartz.service.SimpleFirstService"></bean>
指定了開始延遲時間,重複間隔時間已經重複的次數限制,查看錶以下:測試
mysql> select * from qrtz_simple_triggers; +-------------+--------------------+------------------+--------------+-----------------+-----------------+ | SCHED_NAME | TRIGGER_NAME | TRIGGER_GROUP | REPEAT_COUNT | REPEAT_INTERVAL | TIMES_TRIGGERED | +-------------+--------------------+------------------+--------------+-----------------+-----------------+ | myScheduler | firstSimpleTrigger | firstSimpleGroup | 5 | 2000 | 1 | +-------------+--------------------+------------------+--------------+-----------------+-----------------+
TIMES_TRIGGERED用來記錄執行了多少次了,此值被定義在SimpleTriggerImpl中,每次執行+1,這裏定義的REPEAT_COUNT=5,實際狀況會執行6次,具體能夠查看SimpleTriggerImpl源碼:spa
public Date getFireTimeAfter(Date afterTime) { if (complete) { return null; } if ((timesTriggered > repeatCount) && (repeatCount != REPEAT_INDEFINITELY)) { return null; } ...... }
timesTriggered默認值爲0,當timesTriggered > repeatCount中止trigger,因此會執行6次,當執行完畢以後此記錄會被刪除;code
存儲CalendarIntervalTrigger和DailyTimeIntervalTrigger兩種類型的觸發器,使用CalendarIntervalTrigger作以下配置:
<bean id="firstCalendarTrigger" class="org.quartz.impl.triggers.CalendarIntervalTriggerImpl"> <property name="jobDataMap"> <map> <entry key="jobDetail" value-ref="firstCalendarTask"></entry> </map> </property> <property name="key" ref="calendarTriggerKey"></property> <property name="repeatInterval" value="1" /> <property name="group" value="firstCalendarGroup"></property> </bean> <bean id="firstCalendarTask" class="org.springframework.scheduling.quartz.JobDetailFactoryBean"> <property name="jobClass" value="zh.maven.SQuartz.task.CalendarFirstTask" /> <property name="jobDataMap"> <map> <entry key="firstService" value-ref="calendarFirstService" /> </map> </property> </bean> <bean id="calendarFirstService" class="zh.maven.SQuartz.service.CalendarFirstService"></bean>
CalendarIntervalTrigger沒有對應的FactoryBean,直接設置實現類CalendarIntervalTriggerImpl;指定的重複週期是1,默認單位是天,也就是天天執行一次,查看錶以下:
mysql> select * from qrtz_simprop_triggers; +-------------+--------------------+--------------------+------------+---------------+------------+------------+------------+-------------+-------------+------------+------------+-------------+-------------+ | SCHED_NAME | TRIGGER_NAME | TRIGGER_GROUP | STR_PROP_1 | STR_PROP_2 | STR_PROP_3 | INT_PROP_1 | INT_PROP_2 | LONG_PROP_1 | LONG_PROP_2 | DEC_PROP_1 | DEC_PROP_2 | BOOL_PROP_1 | BOOL_PROP_2 | +-------------+--------------------+--------------------+------------+---------------+------------+------------+------------+-------------+-------------+------------+------------+-------------+-------------+ | myScheduler | calendarTriggerKey | firstCalendarGroup | DAY | Asia/Shanghai | NULL | 1 | 1 | 0 | 0 | NULL | NULL | 0 | 0 | +-------------+--------------------+--------------------+------------+---------------+------------+------------+------------+-------------+-------------+------------+------------+-------------+-------------+
提供了3個string類型的參數,2個int類型的參數,2個long類型的參數,2個decimal類型的參數以及2個boolean類型的參數;具體每一個參數是什麼含義,根據不一樣的trigger類型存放各自的參數;
存儲已經觸發的trigger相關信息,trigger隨着時間的推移狀態發生變化,直到最後trigger執行完成,從表中被刪除;已SimpleTrigger爲例重複3次執行,查詢表:
mysql> select * from qrtz_fired_triggers; +-------------+----------------------------------------+--------------------+------------------+---------------------------+---------------+---------------+----------+-----------+-----------------+-----------+------------------+-------------------+ | SCHED_NAME | ENTRY_ID | TRIGGER_NAME | TRIGGER_GROUP | INSTANCE_NAME | FIRED_TIME | SCHED_TIME | PRIORITY | STATE | JOB_NAME | JOB_GROUP | IS_NONCONCURRENT | REQUESTS_RECOVERY | +-------------+----------------------------------------+--------------------+------------------+---------------------------+---------------+---------------+----------+-----------+-----------------+-----------+------------------+-------------------+ | myScheduler | NJD9YZGJ2-PC15241041777351524104177723 | firstSimpleTrigger | firstSimpleGroup | NJD9YZGJ2-PC1524104177735 | 1524104178499 | 1524104178472 | 0 | EXECUTING | firstSimpleTask | DEFAULT | 0 | 0 | | myScheduler | NJD9YZGJ2-PC15241041777351524104177724 | firstSimpleTrigger | firstSimpleGroup | NJD9YZGJ2-PC1524104177735 | 1524104180477 | 1524104180472 | 0 | EXECUTING | firstSimpleTask | DEFAULT | 0 | 0 | | myScheduler | NJD9YZGJ2-PC15241041777351524104177725 | firstSimpleTrigger | firstSimpleGroup | NJD9YZGJ2-PC1524104177735 | 1524104180563 | 1524104182472 | 0 | ACQUIRED | NULL | NULL | 0 | 0 | +-------------+----------------------------------------+--------------------+------------------+---------------------------+---------------+---------------+----------+-----------+-----------------+-----------+------------------+-------------------+
相同的trigger和task,每觸發一次都會建立一個實例;從剛被建立的ACQUIRED狀態,到EXECUTING狀態,最後執行完從數據庫中刪除;
存儲定義的trigger,以上定義的三個triggers爲例,分別是:firstSimpleTrigger,firstCalendarTrigger和firstCronTrigger,運行以後查看數據庫:
mysql> select * from qrtz_triggers; +-------------+--------------------+--------------------+-------------------+-----------+-------------+----------------+----------------+----------+---------------+--------------+---------------+----------+---------------+---------------+----------+ | SCHED_NAME | TRIGGER_NAME | TRIGGER_GROUP | JOB_NAME | JOB_GROUP | DESCRIPTION | NEXT_FIRE_TIME | PREV_FIRE_TIME | PRIORITY | TRIGGER_STATE | TRIGGER_TYPE | START_TIME | END_TIME | CALENDAR_NAME | MISFIRE_INSTR | JOB_DATA | +-------------+--------------------+--------------------+-------------------+-----------+-------------+----------------+----------------+----------+---------------+--------------+---------------+----------+---------------+---------------+----------+ | myScheduler | calendarTriggerKey | firstCalendarGroup | firstCalendarTask | DEFAULT | NULL | 1524203884719 | 1524117484719 | 5 | WAITING | CAL_INT | 1524117484719 | 0 | NULL | 0 | | | myScheduler | firstCronTrigger | firstCronGroup | firstTask | DEFAULT | NULL | 1524117492000 | 1524117486000 | 0 | ACQUIRED | CRON | 1524117483000 | 0 | firstCalendar | 0 | | | myScheduler | firstSimpleTrigger | firstSimpleGroup | firstSimpleTask | DEFAULT | NULL | -1 | 1524117488436 | 0 | COMPLETE | SIMPLE | 1524117484436 | 0 | NULL | 0 | | +-------------+--------------------+--------------------+-------------------+-----------+-------------+----------------+----------------+----------+---------------+--------------+---------------+----------+---------------+---------------+----------+
和qrtz_fired_triggers存放的不同,無論trigger觸發了多少次都只有一條記錄,TRIGGER_STATE用來標識當前trigger的狀態;firstCalendarTask天天執行一次,執行完以後一直是WAITING狀態;firstCronTrigger每6秒執行一次狀態是ACQUIRED狀態;firstSimpleTrigger重複執行6次後狀態爲COMPLETE,而且會被刪除;
存儲jobDetails信息,相關信息在定義的時候指定,如上面定義的JobDetailFactoryBean,查詢數據庫:
mysql> select * from qrtz_job_details; +-------------+-------------------+-----------+-------------+-----------------------------------------+------------+------------------+----------------+-------------------+----------+ | SCHED_NAME | JOB_NAME | JOB_GROUP | DESCRIPTION | JOB_CLASS_NAME | IS_DURABLE | IS_NONCONCURRENT | IS_UPDATE_DATA | REQUESTS_RECOVERY | JOB_DATA | +-------------+-------------------+-----------+-------------+-----------------------------------------+------------+------------------+----------------+-------------------+----------+ | myScheduler | firstCalendarTask | DEFAULT | NULL | zh.maven.SQuartz.task.CalendarFirstTask | 0 | 0 | 0 | 0 | | | myScheduler | firstSimpleTask | DEFAULT | NULL | zh.maven.SQuartz.task.SimpleFirstTask | 0 | 0 | 0 | 0 | | | myScheduler | firstTask | DEFAULT | NULL | zh.maven.SQuartz.task.FirstTask | 0 | 0 | 0 | 0 | | +-------------+-------------------+-----------+-------------+-----------------------------------------+------------+------------------+----------------+-------------------+----------+
JOB_DATA存放的就是定義task時指定的jobDataMap屬性,因此此屬性須要實現Serializable接口,方便持久化到數據庫;
Quartz爲咱們提供了日曆的功能,能夠本身定義一個時間段,能夠控制觸發器在這個時間段內觸發或者不觸發;如今提供6種類型:AnnualCalendar,CronCalendar,DailyCalendar,HolidayCalendar,MonthlyCalendar,WeeklyCalendar;如下使用CronCalendar爲例:
<bean id="firstCalendar" class="org.quartz.impl.calendar.CronCalendar"> <constructor-arg value="0/5 * * ? * *"></constructor-arg> </bean> <bean id="firstCronTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean"> <property name="jobDetail" ref="firstTask" /> <property name="cronExpression" value="0/6 * * ? * *" /> <property name="group" value="firstCronGroup"></property> <property name="calendarName" value="firstCalendar"></property> </bean> <bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <property name="schedulerName" value="myScheduler"></property> <property name="dataSource" ref="dataSource" /> <property name="configLocation" value="classpath:quartz.properties" /> <property name="triggers"> <list> <ref bean="firstCronTrigger" /> </list> </property> <property name="calendars"> <map> <entry key="firstCalendar" value-ref="firstCalendar"></entry> </map> </property> </bean>
定義了一個排除每隔5秒的CronCalendar,而後在firstCronTrigger中指定了calendarName,而且須要在SchedulerFactoryBean中定義calendars;由於firstCronTrigger每6秒執行一次,而CronCalendar排除每隔5秒,因此會出現firstCronTrigger在第5次觸發的時候須要等待12秒,結果以下:
20180419 15:09:06---start FirstService 20180419 15:09:08---end FirstService 20180419 15:09:12---start FirstService 20180419 15:09:14---end FirstService 20180419 15:09:18---start FirstService 20180419 15:09:20---end FirstService 20180419 15:09:24---start FirstService 20180419 15:09:26---end FirstService 20180419 15:09:36---start FirstService 20180419 15:09:38---end FirstService
查詢保存在數據中的CronCalendar:
mysql> select * from qrtz_calendars; +-------------+---------------+----------+ | SCHED_NAME | CALENDAR_NAME | CALENDAR | +-------------+---------------+----------+ | myScheduler | firstCalendar | | +-------------+---------------+----------+
CALENDAR存放的是CronCalendar序列化以後的數據;
存放暫停掉的觸發器,測試手動暫停firstCronTrigger,代碼以下:
public class App { public static void main(String[] args) { final AbstractApplicationContext context = new ClassPathXmlApplicationContext("quartz.xml"); final StdScheduler scheduler = (StdScheduler) context.getBean("scheduler"); try { Thread.sleep(4000); scheduler.pauseTriggers(GroupMatcher.triggerGroupEquals("firstCronGroup")); } catch (Exception e) { e.printStackTrace(); } } }
啓動以後延遲4秒後暫停firstCronTrigger,這裏傳遞的參數group,而後查看數據庫:
mysql> select * from qrtz_paused_trigger_grps; +-------------+----------------+ | SCHED_NAME | TRIGGER_GROUP | +-------------+----------------+ | myScheduler | firstCronGroup | +-------------+----------------+
由於已經入庫,因此重啓以後firstCronGroup仍是處於暫停狀態,firstCronTrigger不會運行;
存儲全部節點的scheduler,會按期檢查scheduler是否失效,啓動多個scheduler,查詢數據庫:
mysql> select * from qrtz_scheduler_state; +-------------+---------------------------+-------------------+------------------+ | SCHED_NAME | INSTANCE_NAME | LAST_CHECKIN_TIME | CHECKIN_INTERVAL | +-------------+---------------------------+-------------------+------------------+ | myScheduler | NJD9YZGJ2-PC1524209095408 | 1524209113973 | 1000 | | myScheduler | NJD9YZGJ2-PC1524209097649 | 1524209113918 | 1000 | +-------------+---------------------------+-------------------+------------------+
記錄了最後最新的檢查時間,在quartz.properties中設置了CHECKIN_INTERVAL爲1000,也就是每秒檢查一次;
Quartz提供的鎖表,爲多個節點調度提供分佈式鎖,實現分佈式調度,默認有2個鎖:
mysql> select * from qrtz_locks; +-------------+----------------+ | SCHED_NAME | LOCK_NAME | +-------------+----------------+ | myScheduler | STATE_ACCESS | | myScheduler | TRIGGER_ACCESS | +-------------+----------------+
STATE_ACCESS主要用在scheduler按期檢查是否失效的時候,保證只有一個節點去處理已經失效的scheduler;
TRIGGER_ACCESS主要用在TRIGGER被調度的時候,保證只有一個節點去執行調度;
本文對這11張表作了簡要的分析,介紹了每張表具體是用來存儲什麼的,而且給了簡單的實例;其實若是要實現一個trigger的管理系統,其實也就是對這幾張表的維護。