精通Quartz-源码分析-创建任务

Quartz中核心的元素有scheduler, trigger和job, 其中 trigger 和 job 是任务调度的元数据, scheduler 是实际执行调度的控制器; 

本文将首先介绍Job,Trigger相关类及持久化的数据库表, 再通过源码分析创建任务时job和trigger的持久化相关内容。

1.Job介绍

Job:是一个接口,只有一个方法void execute(JobExecutionContext context),开发者实现该接口定义运行任务,JobExecutionContext类提供了调度上下文的各种信息。Job运行时的信息保存在JobDataMap实例中。

JobDetail:用来描述Job实现类及其它相关的静态信息,如Job名字、是否禁止并发(IS_NONCONCURRENT)、是否持久化(IS_DURABLE),是否失败转移(REQUESTS_RECOVERY)等信息。

Job持久化只涉及到一张表:qrtz_job_details 比较简单

2.Trigger介绍

4种类型Trigger

CronTrigger,通过Cron表达式,结合Calendar可以支持各种复杂的调度策略;

SimpleTrigger,指定从某一个时间开始,以一定的时间间隔(单位是毫秒)执行一定次数任务,可以方便的支持到秒、分、时。

CalendarIntervalTrigger,指定从某一个时间开始,以一定的时间间隔执行一定次数任务,支持的间隔单位更全,有秒,分钟,小时,天,月,年,星期

DailyTimeIntervalTrigger,指定每天的某个时间段内,以一定的时间间隔执行任务。并且它可以支持指定星期。它适合的任务类似于:指定每天9:00 至 18:00 ,每隔30秒执行一次

6张Trigger相关表

先介绍5张表,4张表是用于存储trigger具体实例信息,1张表是存储trigger的通用信息,(假设表名前缀是qrtz_)

1. qrtz_cron_triggers,存储CronTrigger基本信息

2. qrtz_simple_triggers,存储SimpleTrigger基本信息

3. qrtz_simprop_triggers,存储DailyTimeIntervalTrigger、CalendarIntervalTrigger基本信息

4. qrtz_blob_triggers, 除了上面介绍的四种Trigger,自定义Trigger 默认会使用此表来存储自定义Trigger的信息

5. qrtz_triggers,存储通用trigger信息;包括上次触发时间、下次触发时间,状态等; 所有类型的Trigger在持久化时都会保存一份通用数据到此表;获取任务、执行任务等都是通过此表来操作的


再介绍另1张表名字中也包含trigger的表,用途与上述5张表完全不一样

6. fired_triggers, 存储所有类型Trigger即将触发或正在触发时所产生的运行时信息

3.Trigger持久化源码分析

Quartz简单实例

先看一个简单的Quartz实例, 例子中定义了相应的Job类,包装Job的JobDetail, 调度策略的Trigger, 例子中使用的是CronTrigger, 然后由scheduler进行调度

public static void main(String[] args) throws Throwable {
    SchedulerFactory factory = new StdSchedulerFactory();
    // 从工厂里面拿到一个scheduler实例
    Scheduler scheduler = factory.getScheduler();
    // 真正执行的任务并不是Job接口的实例,而是用反射的方式实例化的一个JobDetail实例
    JobDetail job = JobBuilder.newJob(MyJob.class).withIdentity("job1", "group1").build();
    // 定义一个触发器,startAt方法定义了任务应当开始的时间
    CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule("0/10 * * * * ?").withMisfireHandlingInstructionDoNothing();
    Trigger trigger = newTrigger().withIdentity("trigger1", "group1").withSchedule(cronScheduleBuilder).build();
    // 将任务和Trigger放入scheduler
    scheduler.scheduleJob(job, trigger);
    scheduler.start();
}复制代码

本文要讲的内容是Quartz是持久化配置时的Trigger是如何持久化的,即项目的 resources/quartz.properties 有类似如下定义:

org.quartz.threadPool.threadCount: 10 
org.quartz.threadPool.threadPriority: 5  
# for cluster
org.quartz.jobStore.tablePrefix: qrtz_
org.quartz.jobStore.class: org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.isClustered: true
org.quartz.jobStore.dataSource:qzDS
   
#============================================================================  
# Configure Datasources  
#============================================================================  
#JDBC驱动  
org.quartz.dataSource.qzDS.driver:com.mysql.jdbc.Driver  
org.quartz.dataSource.qzDS.URL:jdbc:mysql://localhost:3306/quartz  
org.quartz.dataSource.qzDS.user:root  
org.quartz.dataSource.qzDS.password:
org.quartz.dataSource.qzDS.maxConnection:10 

复制代码

QuartzScheduler#scheduleJob

调用Scheduler#scheduleJob()时,Quartz都会将JobDetail和Trigger的信息保存到数据库中,我们来一步一步分析一下trigger的持久化部分的源码实现

scheduler#scheduleJob(job, trigger); Quartz的scheduler最终都委托给了QuartzScheduler, 代码如下

// 先看 Scheduler 中的代码
public Date scheduleJob(JobDetail jobDetail, Trigger trigger)
    throws SchedulerException {
    //  Quartz的scheduler最终都委托给了QuartzScheduler
    return sched.scheduleJob(jobDetail, trigger);
}

// 再看 QuartzScheduler 中的代码
public Date scheduleJob(JobDetail jobDetail,
        Trigger trigger) throws SchedulerException {
    validateState();

    // 此处省略了一部分代码。。。。

    // name, group, jobName, jobGroup的非空检查 等等
    trig.validate();

    Calendar cal = null;
    if (trigger.getCalendarName() != null) {
        cal = resources.getJobStore().retrieveCalendar(trigger.getCalendarName());
    }
    // 计算出trig的下一次触发时间nextFireTime, 因为是新增的所以也可以说是第一次的触发时间
    Date ft = trig.computeFirstFireTime(cal);

    if (ft == null) {
        throw new SchedulerException(
                "Based on configured schedule, the given trigger '" + trigger.getKey() + "' will never fire.");
    }

    // 开始持久化,即调用JobStoreSupport#storeJobAndTrigger, 下个段落将展开分析
    // 抽象类JobStoreSupport实现了Quartz任务持久化接口JobStore,实现了持久化的主要逻辑
    // JobStoreSupport的实现子类主要有JobStoreTX, JobStoreCMT
    // 这些子类基本上都是对获得Connection进行了封装,而没有去覆盖实现持久化任务的逻辑
    resources.getJobStore().storeJobAndTrigger(jobDetail, trig);
   
    // 发送事件通知,可以自定义SchedulerListener来接收处理jobAdded
    notifySchedulerListenersJobAdded(jobDetail);

    // 向QuartzSchedulerThread发送通知,更新signaled为true,通知 QuartzSchedulerThread重新获取待执行的任务列表
    notifySchedulerThread(trigger.getNextFireTime().getTime());

    // 发送事件通知,可以自定义SchedulerListener来接收处理jobScheduled
    notifySchedulerListenersSchduled(trigger);

    return ft;
}复制代码

JobStoreSupport#storeJobAndTrigger

// 本方法开始对Job和Trigger进行持久化
public void storeJobAndTrigger(final JobDetail newJob,
        final OperableTrigger newTrigger) 
    throws JobPersistenceException {
    // 加锁处理 (默认配置)
    executeInLock(
        (isLockOnInsert()) ? LOCK_TRIGGER_ACCESS : null,
        new VoidTransactionCallback() {
            public void executeVoid(Connection conn) throws JobPersistenceException {
                // 保存Job, 比较简单,本文不具体展开了
                storeJob(conn, newJob, false);
                // 保存Trigger, 调用加锁后事务回调的具体方法
                storeTrigger(conn, newTrigger, newJob, false,
                        Constants.STATE_WAITING, false, false);
            }
        });
}
// 被回调的具体干活的方法
protected void storeTrigger(Connection conn,
        OperableTrigger newTrigger, JobDetail job, boolean replaceExisting, String state,
        boolean forceState, boolean recovering)
    throws JobPersistenceException {
    // storeTrigger方法会被多个地方调用,但在保存Trigger的这个场景下existingTrigger肯定是false
    boolean existingTrigger = triggerExists(conn, newTrigger.getKey());

    if ((existingTrigger) && (!replaceExisting)) { 
        throw new ObjectAlreadyExistsException(newTrigger); 
    }
    
    try {
        boolean shouldBepaused;
        
        // 此处省略了一部分代码。。。。

        if (job.isConcurrentExectionDisallowed() && !recovering) { 
            state = checkBlockedState(conn, job.getKey(), state);
        }
        
        if (existingTrigger) {
            getDelegate().updateTrigger(conn, newTrigger, state, job);
        } else {
             // getDelegate()获得所委托的具体实现类即StdJDBCDelegate,见下个段落的分析            
            getDelegate().insertTrigger(conn, newTrigger, state, job);
        }
    } catch (Exception e) {
        throw new JobPersistenceException("Couldn't store trigger '" + newTrigger.getKey() + "' for '" 
                + newTrigger.getJobKey() + "' job:" + e.getMessage(), e);
    }
}
复制代码
// JobStoreSupport#getDelegate 获得任务持久化的委托执行类, 默认返回 StdJDBCDelegate
protected DriverDelegate getDelegate() throws NoSuchDelegateException {
    synchronized(this) {
        // 第一次访问getDelegate方法时delegate肯定为null
        if(null == delegate) {
            try {
                if(delegateClassName != null) {
                    // quartz.properties文件指定了delegateClassName则加载相应的类,默认为null
                    delegateClass = getClassLoadHelper().loadClass(delegateClassName, DriverDelegate.class);
                }
                // JobStoreSupport定义了默认的delegateClass                
                // protected Class<? extends DriverDelegate> delegateClass = StdJDBCDelegate.class;
                // 加载DriverDelegate具体实例
                delegate = delegateClass.newInstance();
                // StdJDBCDelegate 初始化
                delegate.initialize(getLog(), tablePrefix, instanceName, instanceId, getClassLoadHelper(), canUseProperties(), getDriverDelegateInitString());
                
            } catch (InstantiationException e) {
               // 各种catch, 代码省略...
            }
        }
        return delegate;复制代码

StdJDBCDelegate初始化及TriggerPersistenceDelegate列表的初始化

//  StdJDBCDelegate#initialize
public void initialize(Logger logger, String tablePrefix, String schedName, String instanceId, ClassLoadHelper classLoadHelper, boolean useProperties, String initString) throws NoSuchDelegateException {

    this.logger = logger;
    this.tablePrefix = tablePrefix;
    this.schedName = schedName;
    this.instanceId = instanceId;
    this.useProperties = useProperties;
    this.classLoadHelper = classLoadHelper;
    // 添加默认的持久化委托类, 此方法很重要,见下文分析
    addDefaultTriggerPersistenceDelegates();

    if(initString == null)
        return;
    // 默认此时已返回, 如果有自定义的持久化委托,则继续解析initString, 添加到具体的委托类列表中
    String[] settings = initString.split("\\|");
    
    for(String setting: settings) {
        String[] parts = setting.split("=");
        String name = parts[0];
        if(parts.length == 1 || parts[1] == null || parts[1].equals(""))
            continue;

        if(name.equals("triggerPersistenceDelegateClasses")) {
            
            String[] trigDelegates = parts[1].split(",");
            
            for(String trigDelClassName: trigDelegates) {
                try {
                    Class<?> trigDelClass = classLoadHelper.loadClass(trigDelClassName);
                    addTriggerPersistenceDelegate((TriggerPersistenceDelegate) trigDelClass.newInstance());
                } catch (Exception e) {
                    throw new NoSuchDelegateException("Error instantiating TriggerPersistenceDelegate of type: " + trigDelClassName, e);
                } 
            }
        }
        else
            throw new NoSuchDelegateException("Unknown setting: '" + name + "'");
    }
}

// 添加默认的4种Trigger的持久化委托类,所有的委托类都要实现接口方法 canHandleTriggerType
// canHandleTriggerType 方法是确定Trigger由哪一个委托来执行的关键
protected void addDefaultTriggerPersistenceDelegates() {
    addTriggerPersistenceDelegate(new SimpleTriggerPersistenceDelegate());
    addTriggerPersistenceDelegate(new CronTriggerPersistenceDelegate());
    addTriggerPersistenceDelegate(new CalendarIntervalTriggerPersistenceDelegate());
    addTriggerPersistenceDelegate(new DailyTimeIntervalTriggerPersistenceDelegate());
}




复制代码

StdJDBCDelegate#insertTrigger

public int insertTrigger(Connection conn, OperableTrigger trigger, String state,
        JobDetail jobDetail) throws SQLException, IOException {

    ByteArrayOutputStream baos = null;
    if(trigger.getJobDataMap().size() > 0) {
        baos = serializeJobData(trigger.getJobDataMap());
    }
    
    PreparedStatement ps = null;

    int insertResult = 0;

    try {
        // 创建statement保存qrtz_TRIGGERS
        ps = conn.prepareStatement(rtp(INSERT_TRIGGER));
        ps.setString(1, trigger.getKey().getName());
        ps.setString(2, trigger.getKey().getGroup());
        ps.setString(3, trigger.getJobKey().getName());
        ps.setString(4, trigger.getJobKey().getGroup());
        ps.setString(5, trigger.getDescription());
        if(trigger.getNextFireTime() != null)
            ps.setBigDecimal(6, new BigDecimal(String.valueOf(trigger
                    .getNextFireTime().getTime())));
        else
            ps.setBigDecimal(6, null);
        long prevFireTime = -1;
        if (trigger.getPreviousFireTime() != null) {
            prevFireTime = trigger.getPreviousFireTime().getTime();
        }
        ps.setBigDecimal(7, new BigDecimal(String.valueOf(prevFireTime)));
        ps.setString(8, state);
        // 这一句是重点查找当前Trigger的持久化委托类, 委托类中的委托,设计得有点复杂, 在下面段落会有分析
        TriggerPersistenceDelegate tDel = findTriggerPersistenceDelegate(trigger);
        
        String type = TTYPE_BLOB;
        if(tDel != null)
            type = tDel.getHandledTriggerTypeDiscriminator();
        ps.setString(9, type);
        
        // 此处省略了一部分代码。。。。
        
         // 执行保存操作,qrtz_TRIGGERS
        insertResult = ps.executeUpdate();
        
        if(tDel == null)
            // 不是那4种默认Trigger类则执行
            insertBlobTrigger(conn, trigger);
        else
            // 4种Trigger都有各自的实现类
            // CronTriggerPersistenceDelegate 操作的表是qrtz_CRON_TRIGGERS
            // SimpleTriggerPersistenceDelegate 操作的表是qrtz_SIMPLE_TRIGGERS
            // DailyTimeIntervalTriggerPersistenceDelegate和CalendarIntervalTriggerPersistenceDelegate
            // 最后两个实现类都是继承了SimplePropertiesTriggerPersistenceDelegateSupport
            // 最后两个实现类操作的表都是qrtz_SIMPROP_TRIGGERS
            tDel.insertExtendedTriggerProperties(conn, trigger, state, jobDetail);
        
    } finally {
        closeStatement(ps);
    }

    return insertResult;
}
复制代码


扩展:

Quartz的顶层设计各功能模块都可以扩展自定义的,拿本文来说可以扩展实现自己的Trigger, 也可以扩展Trigger的存储方式;

可以自定义一个Trigger,继承上面的4种Trigger, 也可以自定义该Trigger相应的存储TriggerPersistenceDelegate;

自定义的Trigger存储器怎么才能生效呢?

当然是修改quartz.properties文件,增加一行配置,例:

org.quartz.jobStore.driverDelegateInitString: triggerPersistenceDelegateClasses=com.shengu.quartz.trigger.MyPersistenceDelegateClasses复制代码

也可以支持定义多个Trigger存储器,通过上面的源码可以看出来,多个的定义是通过 | 来分隔的;

quartz的各种扩展一般都是通过在quartz.properties中增加配置来实现的


小结:

创建任务时,Job操作一张表 qrtz_job_detail;Trigger会操作两张表

1.保存Trigger通用信息到qrtz_triggers

2.保存Trigger具体实例信息到下面对应的一个表中

2.1 系统默认的4种Trigger分别对应 qrtz_cron_triggers,qrtz_simple_triggers, qrtz_simprop_triggers,qrtz_simprop_triggers

2.2 自定义Trigger默认对应保存到qrtz_blob_triggers 


Trigger存储是支持扩展的,可以自定义Trigger存储在哪张表中


转载于:https://juejin.im/post/5bff8de4f265da61223a0f5e

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值