回到顶部

Quartz.NET 2.x 文档翻译 - Lesson 3:Jobs 和 Triggers深入了解

时间:2年前   作者:请喊我大龙哥   浏览:182   [站内原创,转载请注明出处]

Quartz.NET 2.x 文档翻译 - Lesson 3:Jobs 和 Triggers深入了解 查看官网英文原文

返回目录

正如我们在第2章说到的,我们可以很容易的执行一个job任务,那么接下来需要我们了解知道的job深入内容主要有IJob接口的Execute()方法和JobDetails。

对于一个job任务来说,我们知道如何制定特定type类型和如何编写实现一个job的代码,对于使用Quartz.NET来说我们需要了解它各种各样的属性参数,那么肯定希望有这方面的demo例子了,正如我们上一章说过的,可以通过job的JobDetail明细类来进行分析。

JobDetail明细类的实例是通过JobBuilder创建的,JobBuilder允许我们使用连续的接口方式进行实现和编码。

我们还是用quartz.net的生命周期代码来分析,正如上几章节提到的代码:

Using Quartz.NET

// define the job and tie it to our HelloJob class
IJobDetail job = JobBuilder.Create<HelloJob>()
  .WithIdentity("myJob", "group1")
  .Build();

// Trigger the job to run now, and then every 40 seconds
ITrigger trigger = TriggerBuilder.Create()
  .WithIdentity("myTrigger", "group1")
  .StartNow()
  .WithSimpleSchedule(x => x
    .WithIntervalInSeconds(40)
    .RepeatForever())
  .Build();
  
sched.ScheduleJob(job, trigger);

接下来定义一个job实现类HelloJob:

public class HelloJob : IJob
{
  public void Execute(IJobExecutionContext context)
  {
    Console.WriteLine("HelloJob is executing.");
  }
}

注意这里,我们提供给scheduler调度的是一个IJobDetail接口,通过引用关系,job的实现类引用来执行任务的。每次scheduler调度执行job任务的时候,调度会在执行Execute方法前创建一个新的实现类,然后继续执行方法,这就要求我们每一个job任务实现类都需要一个自身构造函数,另外就是在job任务实例类中定义数据存储字段属性等操作是无意义的,因为job执行过程中并不会进行保存。

说到这里,也许你会想问“那我怎么才能给一个job任务实例化类设置一些属性或者配置呢?”或者“我如何才能跟踪记录job任务状态在执行间隔?”,答案就是:JobDataMap中的key值,也就是JobDetail对象的包含属性内容中。

JobDataMap

JobDataMap可以存储容纳任意数量的可序列化对象,以便在job实例化执行的时候使用。JobDataMap是IDictionary接口的实现,并添加了一些方便存储和检索原始类型数据的方法。

下面演示一小段代码来说明将job任务添加到scheduler调度前先将数据添加到JobDataMap:

Setting Values in a JobDataMap

// define the job and tie it to our DumbJob class
IJobDetail job = JobBuilder.Create<DumbJob>()
  .WithIdentity("myJob", "group1") // name "myJob", group "group1"
  .UsingJobData("jobSays", "Hello World!")
  .UsingJobData("myFloatValue", 3.141f)
  .Build();

下面演示下job任务执行时候从JobDataMap中取出数据的代码片段:

Getting Values from a JobDataMap

public class DumbJob : IJob
{
  public void Execute(JobExecutionContext context)
  {
    JobKey key = context.JobDetail.Key;

    JobDataMap dataMap = context.JobDetail.JobDataMap;

    string jobSays = dataMap.GetString("jobSays");
    float myFloatValue = dataMap.GetFloat("myFloatValue");

    Console.Error.WriteLine("Instance " + key + " of DumbJob says: " + jobSays + ", and val is: " + myFloatValue);
  }
} 

如果想保持持久化的数据,我们需要注意哪些放置到JobDataMap中,因为对象会被序列化,这很容易带来版本问题。很明显,标准的.net类型理论是安全的,但除此之外,已经序列化的类中任意一个属性的变化,都有可能导致对象信息的不完整和不准确。

当然,我们也可以将adojobstore和JobDataMap组合成一个model模型,只能存储string和原始数据库类型,以此来消除可能出现的序列化问题。

如果我们添加设置了和JobDataMap中的key的name值相同的属性到job任务class类中,然后quartz的JobFactory会默认自动调用这些设置属性在job任务类实例化的时候,因此,我们要明确的从执行方法中取出我们需要的数值。

Trigger触发器也有JobDataMaps,在我们需要定时调度执行一个重复性的job任务的时候,就用到Trigger触发器的JobDataMaps了,对于独立的Trigger触发器来说我们就需要提供不同的data数据给job任务。

JobDataMap是在job任务执行过程中由JobExecutionContext当做载体存储的,这其实是Trigger触发器和jobdetail任务详细的一个合并而成的JobDataMap,后者根据相同的name属性重写了对应的value值。

下面演示数据从JobExecutionContext在job执行过程中合并JobDataMap的过程:

public class DumbJob : IJob
{
  public void Execute(IJobExecutionContext context)
  {
    JobKey key = context.JobDetail.Key;

    JobDataMap dataMap = context.MergedJobDataMap;  // Note the difference from the previous example

    string jobSays = dataMap.GetString("jobSays");
    float myFloatValue = dataMap.GetFloat("myFloatValue");
    IList<DateTimeOffset> state = (IList<DateTimeOffset>) dataMap["myStateData"];
    state.Add(DateTimeOffset.UtcNow);

    Console.Error.WriteLine("Instance " + key + " of DumbJob says: " + jobSays + ", and val is: " + myFloatValue);
  }
}

或者我们也可以使用JobFactory的依赖注入方式,将数据映射到class中,如下代码:

public class DumbJob : IJob
{
    public string JobSays { private get; set; }
    public float FloatValue { private get; set; }
      
  public void Execute(IJobExecutionContext context)
  {
    JobKey key = context.JobDetail.Key;

    JobDataMap dataMap = context.MergedJobDataMap;  // Note the difference from the previous example

    IList<DateTimeOffset> state = (IList<DateTimeOffset>) dataMap["myStateData"];
    state.Add(DateTimeOffset.UtcNow);

    Console.Error.WriteLine("Instance " + key + " of DumbJob says: " + JobSays + ", and val is: " + FloatValue);
    }
}
我们发现整体代码量增加了,但是我们的执行方法execute()变得简洁了。还有人认为代码量增加了,但是编码更加容易了,如果代码都是咱们平时的IDE生成的属性等,而不用我们手写代码取出JobDataMap的数值,那么这样的代码才算是整洁有效。

Job “Instances”

很多人可能会有疑惑到底是什么构成了一个个的job任务实例,接下来我们就尽量清晰的讲解清楚job任务的状态和并发等情况。

我们可以创建一个简单的job任务类,然后创建几个实例JobDetails,每个实例都有自己设置的属性和JobDataMap,然后把这些实例添加到scheduler调度。

比如,我们可以创建一个实现了IJob接口的SalesReportJob类,然后添加代码,实现通过传入的参数,如销售人员名称实现将对应销售人员的销售报表发送给他,我们也可以创建很多的JobDetails,比如salesreportforjoe或者salesreportformike来区分joe和mike,然后根据JobDataMaps的参数来区分那个job任务进行执行处理。

当一个Trigger触发器被触发后,JobDetail会被加载,job任务类在通过JobFactory配置实现调度程序。默认JobFactory只调用使用job工作类的默认构造函数。CreateInstance方法会尝试在JobDataMap中调用相匹配的key的names,也许你想创建自己的JobFactory实现,来实现类似于IOC、DI容器等job任务的功能。

对于“Quartz”来说,我们把指定的每个存储JobDetail明细当做是一个job任务类定义或者是JobDetail明细类定义,或是我们执行的每个job任务类定义、job实现定义。

通常我们仅仅使用job任务这个词来表明一个job任务类定义或是一个JobDetail的明细而已。当我们需要实现job接口的时候,我们通常使用“job type”这个词语。

Job State and Concurrency

下面,有一些关于job任务工作状态数据的一些附加说明(又名JobDataMap)和并发的信息,有一对属性可以添加到我们的job任务类中,这一对属性可以影响Quartz的执行并发等方面。

DisallowConcurrentExecution 是其中一个属性,可以添加到job任务类,来通知quartz不同时执行我们给定的一个job任务的多个实例。这里使用的时候一定要注意。在我们前面说过的章节里面,如果SalesReportJob这个job任务类设置了这个属性,那么只能是SalesReportForJoe的一个实例能执行在任务执行时间点,但是我们也可以在执行一个SalesReportForJoe实例的同时执行SalesReportForMike的一个实例。这个约束基于JobDetail的实例而不是job任务类,然而这个约束在quartz的设计过程中又建立在class类本身,因为class的编码有各种不同之处。

PersistJobDataAfterExecution 是另外一个属性,可以实现在Execute()方法执行并成功后通知quartz更新存储从JobDetail的任务明细JobDataMap数据,这样再次执行同一个job任务的时候就会使用update更新后的数据而不是原来的旧数据了。和DisallowConcurrentExecution属性一样,这个属性也是应用在job定义中,而不是job的class中,虽然在我们编码过程中它也是在job的class中去设置的属性。

如果我们使用PersistJobDataAfterExecution属性,我们需要考虑清楚一并使用DisallowConcurrentExecution属性,以此避免多个实例执行job的时候,新旧数据存储的并发问题。

Other Attributes Of Jobs

还有一些JobDetail其他的属性:

Durability - 如果一个job工作是非持久的,那么它会自动从scheduler调度中删除在没有任何活动的triggers触发与它相关联的时候。换言之,非持久的job任务是有生命周期,这取决于它的触发器的是否存在。 

RequestsRecovery - 如果一个job“RequestsRecovery”,它是在调度执行过程中被强制关闭的(比如运行崩溃或是机器断电等),那么它会从新执行在scheduler调度重新启动后。在这种情况下JobExecutionContext.Recovering属性将会返回true。

JobExecutionException

最后,简单说下IJob.Execute(..)方法,该方法执行中的唯一的异常类型是JobExecutionException,因此,在我们编码的时候,需要用try catch将整个方法包含在内,有空的话多看看和JobExecutionException相关的文档,以便在我们使用的时候能更好的执行各种命令并处理各种异常。

返回目录

请喊我大龙哥最后编辑于:2年前

内容均为作者独立观点,不代表八零IT人立场,如涉及侵权,请及时告知。

评论努力加载中...
暂无评论
暂无评论

手机扫码阅读

热门相关

加载中...
关于我们   联系我们   申请友链   赞助记录   站点地图
© 2014 - 2017 www.80iter.com All Rights Reserved. 京ICP备14042174号-1
本站遵循 CC BY 4.0 协议,转载请注明出处 。