Quartz.net官方开发指南 第三课:更多关于Jobs和JobDetails

简介:
如你所见, Job 相当容易实现。这里只是介绍有关 Jobs 本质, I Job 接口的 Execute(..) 方法以及 JobDetails 中需要理解的内容。
在所实现的类成为真正的 Job 时,期望任务所具有的各种属性需要通知给 Quartz 。通过 JobDetail 类可以完成这个工作,这个类在前面的章节中曾简短提及过。现在,我们花一些时间来讨论 Quartz Jobs 的本质和 Job 实例的生命周期。首先让我们回顾一下第一课中所看到的代码片断:
None.gif //  define the job and tie it to our HelloJob class
None.gif
JobDetail job  =   new  JobDetail( " job1 " " group1 " typeof (HelloJob));
None.gif
//  Trigger the job to run on the next round minute
None.gif
SimpleTrigger trigger  =   new  SimpleTrigger( " trigger1 " " group1 " , runTime);
None.gif            
None.gif
//  Tell quartz to schedule the job using our trigger
None.gif
sched.ScheduleJob(job, trigger);
None.gif
现在考虑如下定义的 SimpleJob 类:
None.gif public   class  SimpleJob : IJob
ExpandedBlockStart.gif
{
InBlock.gif        
InBlock.gif        
private static ILog _log = LogManager.GetLogger(typeof(SimpleJob));
InBlock.gif        
ExpandedSubBlockStart.gif        
/// <summary> 
InBlock.gif        
/// Empty constructor for job initilization.
ExpandedSubBlockEnd.gif        
/// </summary>

InBlock.gif        public SimpleJob()
ExpandedSubBlockStart.gif        
{
ExpandedSubBlockEnd.gif        }

InBlock.gif        
ExpandedSubBlockStart.gif        
/// <summary>
InBlock.gif        
/// Called by the <see cref="IScheduler" /> when a
InBlock.gif        
/// <see cref="Trigger" /> fires that is associated with
InBlock.gif        
/// the <see cref="IJob" />.
ExpandedSubBlockEnd.gif        
/// </summary>

InBlock.gif        public virtual void  Execute(JobExecutionContext context)
ExpandedSubBlockStart.gif        
{
InBlock.gif            
// This job simply prints out its job name and the
InBlock.gif            
// date and time that it is running
InBlock.gif
            string jobName = context.JobDetail.FullName;
InBlock.gif            _log.Info(
string.Format("SimpleJob says: {0} executing at {1}", jobName, DateTime.Now.ToString("r")));
ExpandedSubBlockEnd.gif      }

ExpandedBlockEnd.gif}

None.gif
注意,我们给 scheduler 传入了一个 JobDetail 实例,而且这个 JobDetail 实例只是简单提供了类名来引用被执行的 Job 。每次 scheduler 执行这个任务时,它就创建这个类的新实例,然后调用该实例的 Execute(..) 方法。对这种行为的一个推论就是 Job 类必须有一个无参数的构造函数。另外一个推论就是它使得 Job 类中定义的成员数据失去意义,因为这些成员数据值在每次执行的时候被 清空 了。
你可能要问,如何才能为每个 Job 实例提供属性和配置呢?而且,在执行中如何跟踪 Job 的状态呢?这些问题的答案是相同的:关键就是 JobDataMap ,这是 JobDetail 对象的一部分。
JobDataMap
JobDataMap 被用来保存一系列的(序列化的)对象,这些对象在 Job 执行时可以得到。 JobDataMap IDictionary 口的一个实现,而且还增加了一些存储和读取主类型数据的便捷方法。
下面是将 Job 加入到 scheduler 前使用的一些向 JobDataMap 加入数据的方法。
None.gif //  pass initialization parameters into the job
None.gif
job1.JobDataMap.Put(ColorJob.FAVORITE_COLOR,  " Green " );
None.gifjob1.JobDataMap.Put(ColorJob.EXECUTION_COUNT, 
1 );
None.gif
下面的代码展示了在 Job 执行过程中从 JobDataMap  获取数据的代码:
ExpandedBlockStart.gif /// <summary>
InBlock.gif    
/// This is just a simple job that receives parameters and
InBlock.gif    
/// maintains state
InBlock.gif    
/// </summary>
ExpandedBlockEnd.gif    
/// <author>Bill Kratzer</author>

None.gif      public   class  ColorJob : IStatefulJob
ExpandedBlockStart.gif    
{
InBlock.gif        
InBlock.gif        
private static ILog _log = LogManager.GetLogger(typeof(ColorJob));
InBlock.gif        
InBlock.gif        
// parameter names specific to this job
InBlock.gif
        public const string FAVORITE_COLOR = "favorite color";
InBlock.gif        
public const string EXECUTION_COUNT = "count";
InBlock.gif        
InBlock.gif        
// Since Quartz will re-instantiate a class every time it
InBlock.gif        
// gets executed, members non-static member variables can
InBlock.gif        
// not be used to maintain state!
InBlock.gif
        private int _counter = 1;
InBlock.gif        
InBlock.gif    
ExpandedSubBlockStart.gif        
/// <summary>
InBlock.gif        
/// Called by the <see cref="IScheduler" /> when a
InBlock.gif        
/// <see cref="Trigger" /> fires that is associated with
InBlock.gif        
/// the <see cref="IJob" />.
ExpandedSubBlockEnd.gif        
/// </summary>

InBlock.gif        public virtual void Execute(JobExecutionContext context)
ExpandedSubBlockStart.gif        
{
InBlock.gif            
InBlock.gif            
// This job simply prints out its job name and the
InBlock.gif            
// date and time that it is running
InBlock.gif
            string jobName = context.JobDetail.FullName;
InBlock.gif            
InBlock.gif            
// Grab and print passed parameters
InBlock.gif
            JobDataMap data = context.JobDetail.JobDataMap;
InBlock.gif            
string favoriteColor = data.GetString(FAVORITE_COLOR);
InBlock.gif            
int count = data.GetInt(EXECUTION_COUNT);
InBlock.gif            _log.Info(
string.Format("ColorJob: {0} executing at {1}\n  favorite color is {2}\n  execution count (from job map) is {3}\n  execution count (from job member variable) is {4}"
InBlock.gif                jobName, DateTime.Now.ToString(
"r"), favoriteColor, count, _counter));
InBlock.gif            
InBlock.gif            
// increment the count and store it back into the 
InBlock.gif            
// job map so that job state can be properly maintained
InBlock.gif
            count++;
InBlock.gif            data.Put(EXECUTION_COUNT, count);
InBlock.gif            
InBlock.gif            
// Increment the local member variable 
InBlock.gif            
// This serves no real purpose since job state can not 
InBlock.gif            
// be maintained via member variables!
InBlock.gif
            _counter++;
ExpandedSubBlockEnd.gif        }

InBlock.gif
ExpandedBlockEnd.gif    }
 
None.gif
如果使用一个持久的 JobStore (在本指南的 JobStore 章节中讨论),那么必须注意存放在 JobDataMap 中的内容。因为放入 JobDataMap 中的内容将被序列化,而且容易出现类型转换问题。很明显,标准 .NET 类型将是非常安全的,但除此之外的类型,任何时候,只要有人改变了你要序列化其实例的类的定义,就要注意是否打破了程序的兼容性。另外,你可以对 JobStore JobDataMap 采用一种使用模式:就是只把主类型和 String 类型存放在 Map 中,这样就可以减少后面序列化的问题。
有状态和无状态任务
Triggers 也可以有 JobDataMaps 与之相关联。当 scheduler 中的 Job 被多个有规律或者重复触发的 Triggers 所使用时非常有用。对于每次独立的触发,你可为 Job 提供不同的输入数据。
Job 执行时的 JobExecutionContext 中取得 JobDataMap 是惯用手段,它融合了从 JobDetail 和从 Trigger 中获的 JobDataMap ,当有相同名字的键时,它用后者的值覆盖前者值。
StatefulJob —— 有状态任务
现在,一些关于 Job 状态数据的附加论题:一个 Job 实例可以被定义为 有状态的 或者 无状态的 无状态的 任务只拥有它们被加入到 scheduler 时所存储的 JobDataMap 。这意味着,在执行任务过程中任何对 Job Data Map 所作的更改都将丢失而且任务下次执行时也无法看到。你可能会猜想出,有状态的任务恰好相反,它在任务的每次执行之后重新存储 JobDataMap 。有状态任务的一个副作用就是它不能并发执行。换句话说,如果任务有状态,那么当触发器在这个任务已经在执行的时候试图触发它,这个触发器就会被阻塞(等待),直到前面的执行完成。
想使任务有状态,它就要实现IStatefulJob接口而不是实现IJob接口。
Job 'Instances'  任务 实例
这个课程的最终观点或许已经很明确,可以创建一个单独的 Job 类,并且通过创建多个 JobDetails 实例来将它的多个实例存储在 scheduler 中,这样每个 JobDetails 对象都有它自己的一套属性和 JobDataMap ,而且将它们都加入到 scheduler 中。
当触发器被触发的时候,通过 Scheduler 中配置的 JobFactory 来实例化与之关联的 Job 类。缺省的 JobFactory 只是简单地对 Job 类调用GetScheduler ()方法。创建自己 JobFactory 可以利用应用中诸如 Ioc 或者 DI 容器所产生或者初始化的 Job 实例。
obs 的其它属性
这里简短地总结一下通过 JobDetail 对象可以定义 Job 的其它属性。
   •  Durability (持久性) - 如果一个 Job 是不持久的, 一旦没有触发器与之关联,它就会被从 scheduler  中自动删除。
   •  Volatility (无常性) - 如果一个 Job 是无常的 , 在重新启动 Quartz i scheduler  时它不能被保持。  
   •  RequestsRecovery (请求恢复能力)  - 如果一个 Job 具备 请求恢复 能力,当它在执行时遇到 scheduler  硬性的关闭 (例如:执行的过程崩溃,或者计算机被关机),那么当 scheduler 重新启动时,这个任务会被重新执行。这种情况下,JobExecutionContext.Recovering 属性将是true
   •  JobListeners (任务监听器)  - 一个 Job 如果有 0 个或者多个 JobListeners 监听器与之相关联,当这个 Job 执行时,监听器被会被通知。更多有关 JobListeners 的讨论见 TriggerListeners & JobListeners 章节。
 
JobExecutionException  任务执行异常
最后,需要告诉你一些关于 Job.Execute(..) 方法的细节。在 Execute 方法被执行时,仅允许抛出一个 JobExecutionException 类型异常。因此需要将整个要执行的内容包括在一个 'try-catch' 块中。应花费一些时间仔细阅读 JobExecutionException 文档,因为 Job 能够使用它向 scheduler 提供各种指示,你也可以知道怎么处理异常。





本文转自 张善友 51CTO博客,原文链接:http://blog.51cto.com/shanyou/74129,如需转载请自行联系原作者
目录
相关文章