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

如你所见,Job相当容易实现。这里只是介绍有关Jobs本质, IJob接口的Execute(..)方法以及JobDetails中需要理解的内容。

在所实现的类成为真正的“Job”时,期望任务所具有的各种属性需要通知给Quartz。通过JobDetail类可以完成这个工作,这个类在前面的章节中曾简短提及过。现在,我们花一些时间来讨论Quartz中Jobs的本质和Job实例的生命周期。首先让我们回顾一下第一课中所看到的代码片断:

// define the job and tie it to our HelloJob class
JobDetail job = new JobDetail("job1", "group1", typeof(HelloJob));
// Trigger the job to run on the next round minute
SimpleTrigger trigger = new SimpleTrigger("trigger1", "group1", runTime);
            
// Tell quartz to schedule the job using our trigger
sched.ScheduleJob(job, trigger);

现在考虑如下定义的SimpleJob类:

public class SimpleJob : IJob
{
        
        private static ILog _log = LogManager.GetLogger(typeof(SimpleJob));
        
        /// <summary> 
        /// Empty constructor for job initilization.
        /// </summary>
        public SimpleJob()
        {
        }
        
        /// <summary>
        /// Called by the <see cref="IScheduler" /> when a
        /// <see cref="Trigger" /> fires that is associated with
        /// the <see cref="IJob" />.
        /// </summary>
        public virtual void  Execute(JobExecutionContext context)
        {
            // This job simply prints out its job name and the
            // date and time that it is running
            string jobName = context.JobDetail.FullName;
            _log.Info(string.Format("SimpleJob says: {0} executing at {1}", jobName, DateTime.Now.ToString("r")));
      }
}

注意,我们给scheduler传入了一个JobDetail实例,而且这个JobDetail实例只是简单提供了类名来引用被执行的Job。每次scheduler执行这个任务时,它就创建这个类的新实例,然后调用该实例的Execute(..)方法。对这种行为的一个推论就是Job类必须有一个无参数的构造函数。另外一个推论就是它使得Job类中定义的成员数据失去意义,因为这些成员数据值在每次执行的时候被“清空”了。

你可能要问,如何才能为每个Job实例提供属性和配置呢?而且,在执行中如何跟踪Job的状态呢?这些问题的答案是相同的:关键就是JobDataMap,这是JobDetail对象的一部分。

JobDataMap

JobDataMap被用来保存一系列的(序列化的)对象,这些对象在Job执行时可以得到。JobDataMap是IDictionary接口的一个实现,而且还增加了一些存储和读取主类型数据的便捷方法。

下面是将Job加入到scheduler前使用的一些向JobDataMap加入数据的方法。

// pass initialization parameters into the job
job1.JobDataMap.Put(ColorJob.FAVORITE_COLOR, "Green");
job1.JobDataMap.Put(ColorJob.EXECUTION_COUNT, 1);

下面的代码展示了在Job执行过程中从JobDataMap 获取数据的代码:

/// <summary>
    /// This is just a simple job that receives parameters and
    /// maintains state
    /// </summary>
    /// <author>Bill Kratzer</author>
    public class ColorJob : IStatefulJob
    {
        
        private static ILog _log = LogManager.GetLogger(typeof(ColorJob));
        
        // parameter names specific to this job
        public const string FAVORITE_COLOR = "favorite color";
        public const string EXECUTION_COUNT = "count";
        
        // Since Quartz will re-instantiate a class every time it
        // gets executed, members non-static member variables can
        // not be used to maintain state!
        private int _counter = 1;
        
    
        /// <summary>
        /// Called by the <see cref="IScheduler" /> when a
        /// <see cref="Trigger" /> fires that is associated with
        /// the <see cref="IJob" />.
        /// </summary>
        public virtual void Execute(JobExecutionContext context)
        {
            
            // This job simply prints out its job name and the
            // date and time that it is running
            string jobName = context.JobDetail.FullName;
            
            // Grab and print passed parameters
            JobDataMap data = context.JobDetail.JobDataMap;
            string favoriteColor = data.GetString(FAVORITE_COLOR);
            int count = data.GetInt(EXECUTION_COUNT);
            _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}", 
                jobName, DateTime.Now.ToString("r"), favoriteColor, count, _counter));
            
            // increment the count and store it back into the 
            // job map so that job state can be properly maintained
            count++;
            data.Put(EXECUTION_COUNT, count);
            
            // Increment the local member variable 
            // This serves no real purpose since job state can not 
            // be maintained via member variables!
            _counter++;
        }

    }

如果使用一个持久的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提供各种指示,你也可以知道怎么处理异常。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏FreeBuf

scapy在wlan中的应用

Scapy 又是scapy,这是python的一个网络编程方面的库,它在wlan中也有很强大的应用。一般我们买块网卡,然后aircrack-ng套件爆破一下邻居...

374100
来自专栏专注研发

推荐几种Java任务调度的实现

原文:http://www.ibm.com/developerworks/cn/java/j-lo-taskschedule/

25420
来自专栏hbbliyong

Extjs 项目中常用的小技巧,也许你用得着(5)--设置 Ext.data.Store 传参的请求方式

1.extjs 给怎么给panel设背景色 设置bodyStyle:'background:#ffc;padding:10px;', var resultsPa...

31660
来自专栏calvin

【nodejs】让nodejs像后端mvc框架(asp.net mvc)一orm篇【如EF般丝滑】typeorm介绍(8/8)

在使用nodejs开发过程中,刚好碰到需要做一个小工具,需要用到数据库存储功能。而我又比较懒,一个小功能不想搞一个nodejs项目,又搞一个后端项目。不如直接在...

39820
来自专栏Linyb极客之路

分布式之延时任务方案解析

对上述的任务,我们给一个专业的名字来形容,那就是延时任务。那么这里就会产生一个问题,这个延时任务和定时任务的区别究竟在哪里呢?一共有如下几点区别

23430
来自专栏草根专栏

使用xUnit为.net core程序进行单元测试(3)

请使用这个项目作为练习的开始: https://pan.baidu.com/s/1ggcGkGb 测试的分组 打开Game.Tests里面的BossEnemyS...

46250
来自专栏cloudskyme

OTL技术应用

什么是OTL:OTL 是 Oracle, Odbc and DB2-CLI TemplateLibrary 的缩写,是一个操控关系数据库的C++模板库,它目前几...

64260
来自专栏每日一篇技术文章

Foundation-NSLocale

localizedStringForKey("name", defaultValue: "xj",bundleName:"bundle")

6310
来自专栏DOTNET

ASP.NET MVC编程——单元测试

1自动化测试基本概念 自动化测试分为:单元测试,集成测试,验收测试。 单元测试 检验被测单元的功能,被测单元一般为低级别的组件,如一个类或类方法。 单元测试要满...

55650
来自专栏张善友的专栏

ASP.NET Web API 接口执行时间监控

软件产品常常会出现这样的情况:产品性能因某些无法预料的瓶颈而受到干扰,导致程序的处理效率降低,性能得不到充分的发挥。如何快速有效地找到软件产品的性能瓶颈,则是我...

30180

扫码关注云+社区

领取腾讯云代金券