公司項目須要用到任務調度,因此用上了quartz。html
目前是試過兩種spring版本的集成,1.5.9跟2.0.3版本,有所區別。spring
1.5的spring並無提供專門的start啓動類,因此不少東西都要本身手動寫,而2.0提供有start,方便不少。數據庫
quartz工做原理:由schedule來調度一個任務,而任務由trigger和jobdetail組成,jobdetail調用job來實現業務規則,這裏借用一張圖:併發
一般來說,一個jobdetail和一個trigger是對應的,他們將設定同一個group名。trigger有簡單trigger,也有cronTrigger,做用是指定觸發規則(觸發幾回、間隔多少等)。而jobdetail用來指定一個job和部分調度規則,咱們要把咱們實現的業務規則寫到job裏面。jobdetail還有一個做用是給job傳遞一些參數(坑1:遺憾的是隻有基礎類型,你沒法傳遞一個Object)。oracle
坑2:很蛋疼的是,schedule的生成並不參與spring的bean生命週期管理,這意味着你沒法在job裏寫業務規則的時候使用@autowire!爲此,spring提供了專門的工廠類來解決這個問題。app
除此以外,quartz還未trigger、job、schedule提供了響應的監聽器,讓你能夠對他們的生命週期作出響應的處理,好比說一個任務觸發前該作什麼,觸發後/結束後該作什麼。框架
若是你將quartz設置爲持久化到數據庫中,那麼以上的設定都是能夠持久化的(坑3:低版本quartz的job分爲靜態非靜態,靜態job的jobdetail數據沒法持久化到庫中),除了監聽器。ide
想讓監聽器在服務重啓後同樣有效的解決方法也很簡單,配置一個全局的監聽類就能夠了,在監聽類中能夠用group來區別不一樣的調度類型,作出不一樣的處理。函數
坑4:cron類型的觸發和簡單觸發彷佛徹底區別開,簡單觸發能夠指定觸發次數,而cron規則不能指定次數,要指定觸發次數,彷佛只能手動計數,而後在監聽器中手動停掉。oop
首先是2.0的start集成方式:
yml配置
spring: quartz: #相關屬性配置 properties: org: quartz: scheduler: instanceName: quartzScheduler instanceId: AUTO threadPool: class: org.quartz.simpl.SimpleThreadPool threadCount: 10 threadPriority: 5 threadsInheritContextClassLoaderOfInitializingThread: true
這個配置並無實現持久化,也沒有實現集羣,是最簡單的配置。
job須要集成QuartzJobBean
public class MyJob extends QuartzJobBean { @Override protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException { System.out.println("任務啓動"); } }
最後須要一個配置類QuartzConfiguration
@Configuration public class QuartzConfiguration { @Bean public JobDetail myJobDetail() { return JobBuilder.newJob(MyJob.class).withIdentity("myJob").storeDurably().build(); } // 把jobDetail註冊到trigger上去 @Bean public Trigger myJobTrigger() { SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder.simpleSchedule() .withIntervalInSeconds(15).repeatForever(); return TriggerBuilder.newTrigger() .forJob(myJobDetail()) .withIdentity("myJobTrigger") .withSchedule(scheduleBuilder) .build(); } }
很是簡潔,你不須要手動去啓動調度,只須要像這樣,把job、trigger注入,就可使用了。
1.5的使用就繁瑣得多了:
org.quartz.scheduler.instanceName=DefaultQuartzScheduler #調度名稱,全局能夠有多個scheduler對象
org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool #線程池實現類,能夠自定義,org.quartz.simpl.SimpleThreadPool是quartz自帶的。
org.quartz.threadPool.threadCount=20 #可用於併發執行做業的線程數
org.quartz.threadPool.threadPriority=5 #線程權限值,1-10,默認爲5
org.quartz.jobStore.misfireThreshold=60000 #用來設置調度引擎對觸發器超時的忍耐時間,有可能任務觸發時線程池已滿或者調度掛了,但不超過這個時間就不算超時。
org.quartz.jobStore.class= org.quartz.impl.jdbcjobstore.JobStoreTX #指定使用的JobStore,有org.quartz.simpl.RAMJobStore和org.quartz.impl.jdbcjobstore.JobStoreTX,前者表明數據存於內存中,後者表明持久化到數據庫。
org.quartz.jobStore.useProperties=false #若爲true,則JobDataMaps中的全部值都將是String類型,false就可使用全部基本類型。
org.quartz.jobStore.driverDelegateClass= org.quartz.impl.jdbcjobstore.oracle.OracleDelegate #不一樣的數據庫對應不一樣的DriverDelegate
org.quartz.jobStore.tablePrefix=qrtz_ #表前綴,這將體如今數據庫表名上
org.quartz.jobStore.dataSource=qzDS #設置JobStore應該使用哪一個DataSource,這對應着dataSource後面的那個屬性名 org.quartz.dataSource.qzDS.driver= oracle.jdbc.OracleDriver org.quartz.dataSource.qzDS.URL= jdbc:oracle:thin:@192.168.1.1:1521/orclpdb org.quartz.dataSource.qzDS.user= admin org.quartz.dataSource.qzDS.password= admin
#設置數據庫的各項信息
org.quartz.triggerListener.NAME.class = com.ly.cloud.datacollection.quartz.Listener.LoopTaskTriggerListener
#設置監聽器,除此以外還有SchedulerListeners、JobListener能夠配
這裏要注意,這樣配置依舊是未生效的,你必須手動加載配置。
通常來講,scheduler能夠new,也可使用quartz的全局scheduler。
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
這裏要使用配置生成,咱們使用spring來進行配置讀取和bean注入。能夠在spring啓動的時候幹這個事,建立一個ApplicationStartQuartzJobListener,來監聽spring生命週期,當啓動的時候進行scheduler的注入。
@Configuration @Slf4j public class ApplicationStartQuartzJobListener implements ApplicationListener<ContextRefreshedEvent> { @Autowired private QuartzUtils quartzUtils; @Autowired private LoopTaskMapper loopTaskMapper; @Autowired private OrdinaryTaskMapper ordinaryTaskMapper; /** * 初始啓動quartz */ @Override public void onApplicationEvent(ContextRefreshedEvent event) { } /** * 初始注入scheduler * * @return * @throws SchedulerException */ @Bean public Scheduler scheduler(SchedulerFactoryBean schedulerFactoryBean) throws SchedulerException { return schedulerFactoryBean.getScheduler(); } @Bean public MyJobFactory jobFactory() { return new MyJobFactory(); } @Bean public SchedulerFactoryBean schedulerFactoryBean(MyJobFactory jobFactory) throws IOException { SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean(); schedulerFactoryBean.setJobFactory(jobFactory); schedulerFactoryBean.setQuartzProperties(quartzProperties()); return schedulerFactoryBean; } @Bean public Properties quartzProperties() throws IOException { PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean(); propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties")); // 在quartz.properties中的屬性被讀取並注入後再初始化對象 propertiesFactoryBean.afterPropertiesSet(); return propertiesFactoryBean.getObject(); } }
前面說過,job裏是沒法使用@autowire的,解決方法是使用spring提供的SpringBeanJobFactory類。因此咱們還得新建這個。
public class MyJobFactory extends SpringBeanJobFactory {
@Autowired
private AutowireCapableBeanFactory capableBeanFactory; @Override protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception { //調用父類的方法 Object jobInstance = super.createJobInstance(bundle); //進行注入 capableBeanFactory.autowireBean(jobInstance); return jobInstance; } }
這樣,咱們在其餘地方就能夠用@autowire來取這個scheduler了。
@Autowired private Scheduler scheduler;
調度的建立使用大致都是同樣的,trigger、jobDetail、job、listener。
String cronRuler = "* * 4 * ? *"; Date taskStartDate = new Date(); JobDetail jobDetail = JobBuilder .newJob(MyJob.class) .withIdentity("123456", "jobGrounp") .usingJobData("name", "張三") .build(); CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cronRuler); CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity("123456", "jobGrounp") .withSchedule(cronScheduleBuilder) .startAt(taskStartDate) .build();
scheduler.scheduleJob(jobDetail, cronTrigger);
scheduler.start();
usingJobData("name", "張三") 能夠用於將必要的變量(基礎類型)傳遞到job中使用。
JobDataMap dataMap = jobExecutionContext.getJobDetail().getJobDataMap();
String name= dataMap.getString("name");
在job中取觸發時間:
Date date = jobExecutionContext.getFireTime();
在job中再次存取變量:
dataMap.put("fireTimes", fireTimes);
jobExecutionContext.getJobDetail().getJobBuilder().setJobData(dataMap);
監聽器的建立也差很少,繼承實現就行了,可是此處有三個坑,監聽器必須由一個getName屬性返回一個名字,必須有一個無參構造函數,另外全局的listener同樣沒法經過@autowire注入,由於它不參與spring bean的管理!
想要注入只能用ApplicationContext了。
@Component @Slf4j public class MyTriggerListener implements TriggerListener { // 監聽器的名字,必須有個名字 public static final String LISTENER_NAME = "MyTriggerListener"; public MyTriggerListener() { } @Override public String getName() { return LISTENER_NAME; } @Override public void triggerFired(Trigger trigger, JobExecutionContext context) { } @Override public boolean vetoJobExecution(Trigger trigger, JobExecutionContext context) { return false; } @Override @Transactional public void triggerMisfired(Trigger trigger) { } @Override @Transactional public void triggerComplete(Trigger trigger, JobExecutionContext context, CompletedExecutionInstruction triggerInstructionCode) { } }
推薦是寫一個QuartzUtill來管理scheduler的啓動、銷燬等操做。cron表達式建議去https://www.pppet.net/擼一下,以確保準確性。
oracle建表語句,並非全部的表都用到,看你使用的場景,能夠擇選。
DROP TABLE QRTZ_FIRED_TRIGGERS; DROP TABLE QRTZ_PAUSED_TRIGGER_GRPS; DROP TABLE QRTZ_SCHEDULER_STATE; DROP TABLE QRTZ_LOCKS; DROP TABLE QRTZ_SIMPLE_TRIGGERS; DROP TABLE QRTZ_SIMPROP_TRIGGERS; DROP TABLE QRTZ_CRON_TRIGGERS; DROP TABLE QRTZ_BLOB_TRIGGERS; DROP TABLE QRTZ_TRIGGERS; DROP TABLE QRTZ_JOB_DETAILS; DROP TABLE QRTZ_CALENDARS; -- 存儲每個已配置的 Job 的詳細信息 CREATE TABLE qrtz_job_details ( SCHED_NAME VARCHAR2(120) NOT NULL, JOB_NAME VARCHAR2(200) NOT NULL, JOB_GROUP VARCHAR2(200) NOT NULL, DESCRIPTION VARCHAR2(250) NULL, JOB_CLASS_NAME VARCHAR2(250) NOT NULL, IS_DURABLE VARCHAR2(1) NOT NULL, IS_NONCONCURRENT VARCHAR2(1) NOT NULL, IS_UPDATE_DATA VARCHAR2(1) NOT NULL, REQUESTS_RECOVERY VARCHAR2(1) NOT NULL, JOB_DATA BLOB NULL, CONSTRAINT QRTZ_JOB_DETAILS_PK PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP) ); -- 存儲已配置的 Trigger 的信息 CREATE TABLE qrtz_triggers ( SCHED_NAME VARCHAR2(120) NOT NULL, TRIGGER_NAME VARCHAR2(200) NOT NULL, TRIGGER_GROUP VARCHAR2(200) NOT NULL, JOB_NAME VARCHAR2(200) NOT NULL, JOB_GROUP VARCHAR2(200) NOT NULL, DESCRIPTION VARCHAR2(250) NULL, NEXT_FIRE_TIME NUMBER(13) NULL, PREV_FIRE_TIME NUMBER(13) NULL, PRIORITY NUMBER(13) NULL, TRIGGER_STATE VARCHAR2(16) NOT NULL, TRIGGER_TYPE VARCHAR2(8) NOT NULL, START_TIME NUMBER(13) NOT NULL, END_TIME NUMBER(13) NULL, CALENDAR_NAME VARCHAR2(200) NULL, MISFIRE_INSTR NUMBER(2) NULL, JOB_DATA BLOB NULL, CONSTRAINT QRTZ_TRIGGERS_PK PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), CONSTRAINT QRTZ_TRIGGER_TO_JOBS_FK FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP) REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP) ); -- 存儲簡單的 Trigger,包括重複次數,間隔,以及已觸的次數 CREATE TABLE qrtz_simple_triggers ( SCHED_NAME VARCHAR2(120) NOT NULL, TRIGGER_NAME VARCHAR2(200) NOT NULL, TRIGGER_GROUP VARCHAR2(200) NOT NULL, REPEAT_COUNT NUMBER(7) NOT NULL, REPEAT_INTERVAL NUMBER(12) NOT NULL, TIMES_TRIGGERED NUMBER(10) NOT NULL, CONSTRAINT QRTZ_SIMPLE_TRIG_PK PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), CONSTRAINT QRTZ_SIMPLE_TRIG_TO_TRIG_FK FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) ); -- 存儲 Cron Trigger,包括 Cron 表達式和時區信息 CREATE TABLE qrtz_cron_triggers ( SCHED_NAME VARCHAR2(120) NOT NULL, TRIGGER_NAME VARCHAR2(200) NOT NULL, TRIGGER_GROUP VARCHAR2(200) NOT NULL, CRON_EXPRESSION VARCHAR2(120) NOT NULL, TIME_ZONE_ID VARCHAR2(80), CONSTRAINT QRTZ_CRON_TRIG_PK PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), CONSTRAINT QRTZ_CRON_TRIG_TO_TRIG_FK FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) ); CREATE TABLE qrtz_simprop_triggers ( SCHED_NAME VARCHAR2(120) NOT NULL, TRIGGER_NAME VARCHAR2(200) NOT NULL, TRIGGER_GROUP VARCHAR2(200) NOT NULL, STR_PROP_1 VARCHAR2(512) NULL, STR_PROP_2 VARCHAR2(512) NULL, STR_PROP_3 VARCHAR2(512) NULL, INT_PROP_1 NUMBER(10) NULL, INT_PROP_2 NUMBER(10) NULL, LONG_PROP_1 NUMBER(13) NULL, LONG_PROP_2 NUMBER(13) NULL, DEC_PROP_1 NUMERIC(13,4) NULL, DEC_PROP_2 NUMERIC(13,4) NULL, BOOL_PROP_1 VARCHAR2(1) NULL, BOOL_PROP_2 VARCHAR2(1) NULL, CONSTRAINT QRTZ_SIMPROP_TRIG_PK PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), CONSTRAINT QRTZ_SIMPROP_TRIG_TO_TRIG_FK FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) ); -- Trigger 做爲 Blob 類型存儲(用於 Quartz 用戶用 JDBC 建立他們本身定製的 Trigger 類型,<span style="color:#800080;">JobStore</span> 並不知道如何存儲實例的時候) CREATE TABLE qrtz_blob_triggers ( SCHED_NAME VARCHAR2(120) NOT NULL, TRIGGER_NAME VARCHAR2(200) NOT NULL, TRIGGER_GROUP VARCHAR2(200) NOT NULL, BLOB_DATA BLOB NULL, CONSTRAINT QRTZ_BLOB_TRIG_PK PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), CONSTRAINT QRTZ_BLOB_TRIG_TO_TRIG_FK FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) ); -- 以 Blob 類型存儲 Quartz 的 Calendar 信息 CREATE TABLE qrtz_calendars ( SCHED_NAME VARCHAR2(120) NOT NULL, CALENDAR_NAME VARCHAR2(200) NOT NULL, CALENDAR BLOB NOT NULL, CONSTRAINT QRTZ_CALENDARS_PK PRIMARY KEY (SCHED_NAME,CALENDAR_NAME) ); -- 存儲已暫停的 Trigger 組的信息 CREATE TABLE qrtz_paused_trigger_grps ( SCHED_NAME VARCHAR2(120) NOT NULL, TRIGGER_GROUP VARCHAR2(200) NOT NULL, CONSTRAINT QRTZ_PAUSED_TRIG_GRPS_PK PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP) ); -- 存儲與已觸發的 Trigger 相關的狀態信息,以及相聯 Job 的執行信息 CREATE TABLE qrtz_fired_triggers ( SCHED_NAME VARCHAR2(120) NOT NULL, ENTRY_ID VARCHAR2(95) NOT NULL, TRIGGER_NAME VARCHAR2(200) NOT NULL, TRIGGER_GROUP VARCHAR2(200) NOT NULL, INSTANCE_NAME VARCHAR2(200) NOT NULL, FIRED_TIME NUMBER(13) NOT NULL, SCHED_TIME NUMBER(13) NOT NULL, PRIORITY NUMBER(13) NOT NULL, STATE VARCHAR2(16) NOT NULL, JOB_NAME VARCHAR2(200) NULL, JOB_GROUP VARCHAR2(200) NULL, IS_NONCONCURRENT VARCHAR2(1) NULL, REQUESTS_RECOVERY VARCHAR2(1) NULL, CONSTRAINT QRTZ_FIRED_TRIGGER_PK PRIMARY KEY (SCHED_NAME,ENTRY_ID) ); -- 存儲少許的有關 Scheduler 的狀態信息,和別的 Scheduler 實例(假如是用於一個集羣中) CREATE TABLE qrtz_scheduler_state ( SCHED_NAME VARCHAR2(120) NOT NULL, INSTANCE_NAME VARCHAR2(200) NOT NULL, LAST_CHECKIN_TIME NUMBER(13) NOT NULL, CHECKIN_INTERVAL NUMBER(13) NOT NULL, CONSTRAINT QRTZ_SCHEDULER_STATE_PK PRIMARY KEY (SCHED_NAME,INSTANCE_NAME) ); -- 存儲程序的悲觀鎖的信息(假如使用了悲觀鎖) CREATE TABLE qrtz_locks ( SCHED_NAME VARCHAR2(120) NOT NULL, LOCK_NAME VARCHAR2(40) NOT NULL, CONSTRAINT QRTZ_LOCKS_PK PRIMARY KEY (SCHED_NAME,LOCK_NAME) ); create index idx_qrtz_j_req_recovery on qrtz_job_details(SCHED_NAME,REQUESTS_RECOVERY); create index idx_qrtz_j_grp on qrtz_job_details(SCHED_NAME,JOB_GROUP); create index idx_qrtz_t_j on qrtz_triggers(SCHED_NAME,JOB_NAME,JOB_GROUP); create index idx_qrtz_t_jg on qrtz_triggers(SCHED_NAME,JOB_GROUP); create index idx_qrtz_t_c on qrtz_triggers(SCHED_NAME,CALENDAR_NAME); create index idx_qrtz_t_g on qrtz_triggers(SCHED_NAME,TRIGGER_GROUP); create index idx_qrtz_t_state on qrtz_triggers(SCHED_NAME,TRIGGER_STATE); create index idx_qrtz_t_n_state on qrtz_triggers(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP,TRIGGER_STATE); create index idx_qrtz_t_n_g_state on qrtz_triggers(SCHED_NAME,TRIGGER_GROUP,TRIGGER_STATE); create index idx_qrtz_t_next_fire_time on qrtz_triggers(SCHED_NAME,NEXT_FIRE_TIME); create index idx_qrtz_t_nft_st on qrtz_triggers(SCHED_NAME,TRIGGER_STATE,NEXT_FIRE_TIME); create index idx_qrtz_t_nft_misfire on qrtz_triggers(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME); create index idx_qrtz_t_nft_st_misfire on qrtz_triggers(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_STATE); create index idx_qrtz_t_nft_st_misfire_grp on qrtz_triggers(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_GROUP,TRIGGER_STATE); create index idx_qrtz_ft_trig_inst_name on qrtz_fired_triggers(SCHED_NAME,INSTANCE_NAME); create index idx_qrtz_ft_inst_job_req_rcvry on qrtz_fired_triggers(SCHED_NAME,INSTANCE_NAME,REQUESTS_RECOVERY); create index idx_qrtz_ft_j_g on qrtz_fired_triggers(SCHED_NAME,JOB_NAME,JOB_GROUP); create index idx_qrtz_ft_jg on qrtz_fired_triggers(SCHED_NAME,JOB_GROUP); create index idx_qrtz_ft_t_g on qrtz_fired_triggers(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP); create index idx_qrtz_ft_tg on qrtz_fired_triggers(SCHED_NAME,TRIGGER_GROUP);
引用:
Quartz Scheduler misfireThreshold屬性的意義與觸發器超時後的處理策略