@DisallowConcurrentExecution
SimpleTrigger
:简单触发器,从某个时间开始,每隔多少时间触发,重复多少次。
CronTrigger
:使用cron表达式定义触发的时间规则,如"0 0 0,2,4 1/1 * ? *" 表示每天的0,2,4点触发。
DailyTimeIntervalTrigger
:每天中的一个时间段,每N个时间单元触发,时间单元可以是毫秒,秒,分,小时
CalendarIntervalTrigger
:每N个时间单元触发,时间单元可以是毫秒,秒,分,小时,日,月,年。
WAITING,ACQUIRED,EXECUTING,COMPLETE,BLOCKED,ERROR,PAUSED,PAUSED_BLOCKED,DELETED
处理策略
|
描述
|
MISFIRE_INSTRUCTION_FIRE_ONCE_NOW
|
立即触发一次
|
MISFIRE_INSTRUCTION_DO_NOTHING
|
忽略,不处理,等待下次触发
|
<bean id="quartzJobFactory" class="com.taobao.trip.hcs.service.gubei.job.AutowiringSpringBeanJobFactory">
<property name="ignoredUnknownProperties" value="applicationContext"/>
bean>
<bean id="quartzScheduler"
class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="jobFactory" ref="quartzJobFactory"/>
<property name="dataSource" ref="dataSource" />
<property name="quartzProperties">
<props>
<prop key="org.quartz.scheduler.instanceName">${quartz.scheduler.instanceName}}prop>
<prop key="org.quartz.scheduler.instanceId">hcs-crawlprop>
<prop key="org.quartz.threadPool.class">org.quartz.simpl.SimpleThreadPoolprop>
<prop key="org.quartz.threadPool.threadCount">20prop>
<prop key="org.quartz.threadPool.threadPriority">5prop>
<prop key="org.quartz.jobStore.class">org.quartz.impl.jdbcjobstore.JobStoreTXprop>
<prop key="org.quartz.jobStore.misfireThreshold">120000prop>
<prop key="org.quartz.jobStore.tablePrefix">QRTZ_prop>
props>
property>
<property name="schedulerName" value="${quartz.scheduler.schedulerName}" />
<property name="startupDelay" value="30" />
<property name="applicationContextSchedulerContextKey" value="applicationContextKey" />
<property name="overwriteExistingJobs" value="true" />
<property name="autoStartup" value="true" />
<property name="triggers">
<list>
<ref bean="cronTrigger" />
<ref bean="simpleTrigger" />
list>
property>
<property name="jobDetails">
<list>
<ref bean="jobDetail" />
list>
property>
bean>
<bean id="jobDetail" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="name" value="testJob" />
<property name="jobClass" value="com.taobao.trip.hcs.service.common.UniversalActionJob"/>
bean>
<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="jobDetail" ref="jobDetail1" />
<property name="cronExpression" value="0/10 * * * * ?" />
bean>
<bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean">
<property name="repeatInterval" value="30000" />
<property name="repeatCount" value="1000" />
<property name="jobDetail" ref="jobDetail1" />
bean>
public class AutowiringSpringBeanJobFactory
extends SpringBeanJobFactory implements ApplicationContextAware {
private transient AutowireCapableBeanFactory beanFactory;
public void setApplicationContext(final ApplicationContext context) {
beanFactory = context.getAutowireCapableBeanFactory();
}
@Override
public Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
final Object job = super.createJobInstance(bundle);
beanFactory.autowireBean(job); //the magic is done here
return job;
}
}
表名
|
描述
|
QRTZ_CRON_TRIGGERS
|
存储CronTrigger,包括Cron表达式和时区信息
|
QRTZ_FIRED_TRIGGERS
|
存储与已触发的Trigger相关的状态信息,以及相联Job的执行信息
|
QRTZ_PAUSED_TRIGGER_GRPS
|
存储已暂停的Trigger组的信息
|
QRTZ_JOB_DETAILS
|
存储每一个已配置的Job的详细信息
|
QRTZ_SIMPLE_TRIGGERS
|
存储简单的Trigger,包括重复次数、间隔、以及已触的次数
|
QRTZ_TRIGGERS
|
存储所有的Trigger的详细信息:job名称,trigger类型、状态、下次触发时间
|
QRTZ_LOCKS
|
存储行锁的表
|
QuartzSchedulerThread
run()方法:
@Override
public void run() {
boolean lastAcquireFailed = false;
while (!halted.get()) {
int availThreadCount = qsRsrcs.getThreadPool().blockForAvailableThreads();
if(availThreadCount > 0) {
...
// 获取时间idleWaitTime(默认30s)内一定数量的trigger,距下次触发时间不超过TimeWindow(默认0)
// 同时修改trigger的状态,从WAITING更新为ACQUIRED
triggers = qsRsrcs.getJobStore().acquireNextTriggers(
now + idleWaitTime, Math.min(availThreadCount, qsRsrcs.getMaxBatchSize()), qsRsrcs.getBatchTimeWindow());
...
// 这里修改trigger的状态,从ACQUIRED改为EXECUTING,更新trigger下次触发时间
// 再把状态改为WAITING或者BLOCKED或者COMPLETE
List<TriggerFiredResult> res = qsRsrcs.getJobStore().triggersFired(triggers);
...
// 创建具体执行任务的JobRunShell
shell = qsRsrcs.getJobRunShellFactory().createJobRunShell(bndle);
...
// 提交给线程池执行任务
qsRsrcs.getThreadPool().runInThread(shell)
}
} // while (!halted)
// drop references to scheduler stuff to aid garbage collection...
qs = null;
qsRsrcs = null;
}
public void run() {
...
// 从上下文获取任务
Job job = jec.getJobInstance();
...
// 任务实际执行
job.execute(jec);
...
// 执行结束,修改trigger状态
qs.notifyJobStoreJobComplete(trigger, jobDetail, instCode);
}
<prop key="org.quartz.jobStore.class">org.quartz.impl.jdbcjobstore.JobStoreTXprop>
protected <T> T executeInNonManagedTXLock(
String lockName,
TransactionCallback<T> txCallback, final TransactionValidator<T> txValidator) throws JobPersistenceException {
boolean transOwner = false;
Connection conn = null;
try {
if (lockName != null) {
// If we aren't using db locks, then delay getting DB connection
// until after acquiring the lock since it isn't needed.
if (getLockHandler().requiresConnection()) {
conn = getNonManagedTXConnection();
}
transOwner = getLockHandler().obtainLock(conn, lockName);
}
if (conn == null) {
conn = getNonManagedTXConnection();
}
final T result = txCallback.execute(conn);
try {
commitConnection(conn);
} catch (JobPersistenceException e) {
rollbackConnection(conn);
if (txValidator == null || !retryExecuteInNonManagedTXLock(lockName, new TransactionCallback<Boolean>() {
@Override
public Boolean execute(Connection conn) throws JobPersistenceException {
return txValidator.validate(conn, result);
}
})) {
throw e;
}
}
Long sigTime = clearAndGetSignalSchedulingChangeOnTxCompletion();
if(sigTime != null && sigTime >= 0) {
signalSchedulingChangeImmediately(sigTime);
}
return result;
} catch (JobPersistenceException e) {
rollbackConnection(conn);
throw e;
} catch (RuntimeException e) {
rollbackConnection(conn);
throw new JobPersistenceException("Unexpected runtime exception: "
+ e.getMessage(), e);
} finally {
try {
releaseLock(lockName, transOwner);
} finally {
cleanupConnection(conn);
}
}
}
public boolean obtainLock(Connection conn, String lockName)
throws LockException {
if(log.isDebugEnabled()) {
log.debug(
"Lock '" + lockName + "' is desired by: "
+ Thread.currentThread().getName());
}
if (!isLockOwner(lockName)) {
executeSQL(conn, lockName, expandedSQL, expandedInsertSQL);
if(log.isDebugEnabled()) {
log.debug(
"Lock '" + lockName + "' given to: "
+ Thread.currentThread().getName());
}
getThreadLocks().add(lockName);
//getThreadLocksObtainer().put(lockName, new
// Exception("Obtainer..."));
} else if(log.isDebugEnabled()) {
log.debug(
"Lock '" + lockName + "' Is already owned by: "
+ Thread.currentThread().getName());
}
return true;
}
SELECT * FROM t WHERE SCHED_NAME = 'SCHED_NAME' AND LOCK_NAME = 'TRIGGER_ACCESS' FOR UPDATE;