作业流程是在调度器的统一调度下完成的,它可以调度多个作业,触发器提供作业执行的条件(每天 8:00 am),触发器与作业关联,它们是 1:N 的关系,1个触发器可以关联1个或多个作业。
附带的作业
我们知道要实现自己的作业功能只要继承 IJob 接口并实现 Execute(JobExecutionContext context) 方法,再把它添加到调度器,调度器会调用执行Execute(JobExecutionContext context) 方法。调用期间,调度器会跟踪作业和它们的执行次数。Quartz.NET 默认提供了 FileScanJob 监视某个文件是否被修改,NativeJob 执行指定程序,NoOpJob 空操作用来给系统调用 ITriggerListener 、IJobListener ,以及 SendMailJob 邮件发送作业
![](https://img-my.csdn.net/uploads/201303/04/1362363885_8612.png)
它有一个存储作业执行时数据的重要属性:MergedJobDataMap,它是 JobDataMap 类型,也可以使用 JobExecutionContext.JobDetail.JobDataMap 来获得 JobDataMap 对象的引用。作为传递参数的容器,JobDataMap 间接继承了 DirtyFlagMap,DirtyFlagMap 内嵌了 Hashtable 容器,它有一组数据读写方法。可以使用 Visual Studio 的类图来查看。
当实现一个真实的作业类时,一些属性是需要告知Quartz的,也是您希望该作业需要有的。这就需要通过JobDetail类,这个类在前面我们已经简单介绍过。
下面我们看一下作业的本质,以及生命周期。还是看看前面的例子:
Lesson1// construct a scheduler factory ISchedulerFactory schedFact = new StdSchedulerFactory(); // get a scheduler IScheduler sched = schedFact.GetScheduler(); sched.Start(); // construct job info JobDetail jobDetail = new JobDetail("myJob", null, typeof(DumbJob)); // fire every hour Trigger trigger = TriggerUtils.MakeHourlyTrigger(); // start on the next even hour trigger.StartTime = TriggerUtils.GetEvenHourDate(DateTime.UtcNow); trigger.Name = "myTrigger"; sched.ScheduleJob(jobDetail, trigger);
作业类"DumbJob":
public class DumbJob : IJob
{
}
如上例所示,向scheduler提供JobDetail实例,就能指向作业执行。scheduler每次执行(Execute方法)作业前,都新生成一个作业实例。由于是没有参数的构造器,每次作业执行,数据都没清一次。
JobDataMap
以下是添加到scheduler之前,向JobDataMap存放数据的代码片段:
jobDetail.JobDataMap[ "jobSays" ] = "Hello World!" ;
jobDetail.JobDataMap[ "myFloatValue" ] = 3.141f;
jobDetail.JobDataMap[ "myStateData" ] = new ArrayList();
以下是在作业执行时,取数据的代码片段:
public class DumbJob : IJob
{
}
在作业执行时,JobDataMap可以从JobExecutionContext中获得。这时候它是JobDetail和Trigger中的JobDataMap合并,是后来同名对象的覆盖。
下面是在作业执行时,从JobExecutionContext中获得JobDataMap的代码片段:
public class DumbJob : IJob
{
}
有状态与无状态的作业
以上我们的作业实例都是从 IJob 继承,Quartz.NET 里还有 IStatefulJob 、IInterruptableJob,它的声明方式为:
![](https://i-blog.csdnimg.cn/blog_migrate/34031c708bfe702fe82d01ff5c6593aa.gif)
public interface IStatefulJob : IJob
{
}
public interface IInterruptableJob : IJob
{
void Interrupt();
}
IInterruptableJob 接口提供了一个中断方法,但是 IStatefulJob 没有自己的方法。从 Quartz.NET 官方了解到:
一个 Job 实例可以被定义为“有状态的”或者“无状态的”。在执行无状态的任务过程中任何对 JobDataMap 所作的更改都将丢失。有状态的任务恰好相反,它在任务的每次执行之后重新存储 JobDataMap 。有状态任务的一个缺点就是它不能并发执行。也就是说,如果任务有状态,那么当触发器试图触发它,触发器就会被阻塞直到前面的执行完成。想使任务有状态,它就要实现 IStatefulJob 接口而不是实现IJob接口。
作业实例,可以被指定为“statefull”,或者“non-statefull”。Non-statefull作业只在JobDataMap添加到scheduler时有数据。就是意味着,在下一次作业执行时,任何作业状态数据(JobDataMap)的修改丢将丢失或看不见。正如您猜测的那样,statefull作业正好相反,作业的每次执行,JobDataMap都会重新保存,这给statefull作业的负作用,就是不能并发执行。也就是说,如果一个statefull作业,触发器已经触发正在执行了,下个触发器将会被阻塞,直到前一个执行完成。
Job 'Instances'
Other Attributes Of Jobs
这里列一下JobDetail对象的其他属性:
- Durable - 一个non-durable作业, 当没有与之关联的active trigger时,会自动从scheduler中删除的。
- Volatile - 一个volatile作业, 在re-starts scheduler之间是不保留的。
- RequestsRecovery - 一个"requests recovery"作业,在scheduler“hard shutdown”的情况下,或者机器断电的时候,当scheduler重启时,作业重新执行。JobExecutionContext.isRecovering()返回true。
- JobListeners - 作业可以没有或多个JobListeners。作业执行时,listeners会被提醒。更多讨论后面专门讨论。
The Job.Execute(..) Method
为什么作业和触发分开
很多作业调度程序,作业和触发没有明显的分开界限。有些作业定义为一个作业识别号和相应的执行时间。还有些更像Quartz.NET作业和触发的聚合。当开发Quartz的时候,Quartz team决定,把调度和所要执行的动作分开(trigger)。据我们所知,这将带来很多便捷。例如:
- 作业可以独立trigger存储在scheduler中,多个trigger关联同一个作业。
- 松耦合可以重新调度已经过期作业,而不需要重新定义。也可以修改替换触发器而不需要重新定义与之相关的作业。
识别符Identifiers
与scheduler注册作业和触发器时,需要指定识别名称的。作业、触发器也能够安祖存放,为日后维护时组织分类作业和触发器提供方便。作业和触发器的名称在组内必须是唯一的。也就是说,正确的识别符是名称 + 组,如果让组名(Job or Trigger)留空 'null', 就等于说属于SchedulerConstants.DefaultGroup。