译者注:

目录在这 Quartz.NET 3.x 教程

原文在这 Lesson 3: More About Jobs & JobDetails

正如你在 教程 2 中看到的, 作业相当容易实现. 你还需要了解关于作业性质, IJob 接口的 Execute(..) 方法, 以及 JobDetails 这些内容.

虽然你实现的作业类具有知道如何执行特定类型作业实际工作的代码, 但 Quartz.NET 有你可能需要希望的作业实例的的各种属性. 这是通过 JobDetail 类完成的, 这在上一节中简单提到过.

JobDetail 实例是使用 JobBuilder 类来构建的. JobBuilder 允许你使用流式接口来描述你的作业详情.

现在让我们花点时间来讨论一下关于 Quartz.NET 中作业的 '性质' 以及作业实例的生命周期. 首先我们来回顾一下我们在 课程 1 中看到的代码片段:

使用 Quartz.NET

// 定义作业并将其绑定到我们的 HelloJob 类
IJobDetail job = JobBuilder.Create<HelloJob>()
.WithIdentity("myJob", "group1")
.Build(); // 立即触发作业运行, 然后每40秒运行一次
ITrigger trigger = TriggerBuilder.Create()
.WithIdentity("myTrigger", "group1")
.StartNow()
.WithSimpleSchedule(x => x
.WithIntervalInSeconds(40)
.RepeatForever())
.Build(); sched.ScheduleJob(job, trigger);

与此同时定义的作业类 HelloJob 如下:

public class HelloJob : IJob
{
public async Task Execute(IJobExecutionContext context)
{
await Console.Out.WriteLineAsync("HelloJob is executing.");
}
}

作业数据对象

JobDataMap 可用于保存任意数量的 (可序列化的) 对象, 这些对象希望在作业实例执行时对其可用. JobDataMap 是 IDictionary 接口的一个实现, 她为存储和检索原始类型的数据增加了一些便捷的的方法.

在 JobDataMap 中设置值

以下是在将作业添加到调度程序之前将数据放入 JobDataMap 的一些快速片段:

// 定义作业并将其绑定到我们的 DumbJob 类
IJobDetail job = JobBuilder.Create<DumbJob>()
.WithIdentity("myJob", "group1") // name "myJob", group "group1"
.UsingJobData("jobSays", "Hello World!")
.UsingJobData("myFloatValue", 3.141f)
.Build();

以下是一个在作业执行期间从 JobDataMap 获取数据的简单示例:

从 JobDataMap 中获取值

public class DumbJob : IJob
{
public async Task Execute(IJobExecutionContext context)
{
JobKey key = context.JobDetail.Key; JobDataMap dataMap = context.JobDetail.JobDataMap; string jobSays = dataMap.GetString("jobSays");
float myFloatValue = dataMap.GetFloat("myFloatValue"); await Console.Error.WriteLineAsync("Instance " + key + " of DumbJob says: " + jobSays + ", and val is: " + myFloatValue);
}
}

如果你使用持久的 JobStore (在本教程的 JobStore 一节中讨论过), 那么在决定将什么放到 JobDataMap 中时应该格外注意, 因为其中的对象将被序列化, 因此它们很容易出现类版本控制问题. 显然标准的 .NET 类型应该是非常安全的, 但除此之外, 每当有人更改你已序列化实例的类的定义时, 必须注意不能破坏兼容性.

或者, 你可以将 AdoJobStore 和 JobDataMap 设置为只能在映射中存储原语和字符串的模式,从而消除以后序列化问题的任何可能性.

如果您将job访问器的属性添加到作业类中,并与JobDataMap中的键的名称相对应,那么当作业实例化时,Quartz的默认JobFactory实现将自动调用这些设置器,从而避免需要显式地将值在执行方法内映射。

如果你将带有 set 访问器的属性添加到与 JobDataMap 中键的名称相对应的作业类中,那么 Quartz 中默认的 JobFactory 实现将在作业实例化时自动调用这些 setters, 从而避免在执行方法中需要显式地从映射中获取值. 注意当使用自定义JobFactory 时默认情况下不维护此功能.

触发器也可以有与之关联的 JobDataMaps. 如果你有一个作业存储在调度器中, 供多个触发器定期/重复使用, 但对于每个独立的触发器, 你都希望为作业提供不同的数据输入, 则此功能非常有用.

作业执行期间可以非常便利的在 JobExecutionContext 中找到的 JobDataMap. 它是 JobDetail 上的 JobDataMap 和触发器上的 JobDataMap 的并集, 后者中的值将覆盖前者中的任何同名值.

以下是一个在作业执行期间从 JobExecutionContext 中获取合并后的 JobDataMap 数据的简单示例:

public class DumbJob : IJob
{
public async Task Execute(IJobExecutionContext context)
{
JobKey key = context.JobDetail.Key; JobDataMap dataMap = context.MergedJobDataMap; // 注意和上一个示例的区别 string jobSays = dataMap.GetString("jobSays");
float myFloatValue = dataMap.GetFloat("myFloatValue");
IList<DateTimeOffset> state = (IList<DateTimeOffset>)dataMap["myStateData"];
state.Add(DateTimeOffset.UtcNow); await Console.Error.WriteLineAsync("Instance " + key + " of DumbJob says: " + jobSays + ", and val is: " + myFloatValue);
}
}

或者, 如果你希望依赖 JobFactory 将数据映射值 "注入" 到你的类中, 那么它可能看起来像这样:

public class DumbJob : IJob
{
public string JobSays { private get; set; }
public float FloatValue { private get; set; } public async Task Execute(IJobExecutionContext context)
{
JobKey key = context.JobDetail.Key; JobDataMap dataMap = context.MergedJobDataMap; // 注意和上一个例子的区别 IList<DateTimeOffset> state = (IList<DateTimeOffset>)dataMap["myStateData"];
state.Add(DateTimeOffset.UtcNow); await Console.Error.WriteLineAsync("Instance " + key + " of DumbJob says: " + JobSays + ", and val is: " + FloatValue);
}
}

你会注意到类的整体代码较长, 但是 Execute() 方法中的代码会更简洁. 也有人可能会争辩说, 虽然代码较长, 但如果使用 IDE 中自动生成属性, 而不是手工编写从 JobDataMap 中检索值的单个调用, 则实际上所需的代码更少. 选择权在你.

作业 “实例”

许多用户把时间花在了 "作业实例" 的确切构成的困惑中. 在这里和下面的部分中我们将尝试介绍关于作业状态和并发性的内容.

你可以创建一个单一的作业类, 把它的许多 “实例定义” 存储在创建了 JobDetails 的多个实例的调度程序中.

  • 每个都有自己的一组属性和 JobDataMap - 并将它们全部添加到调度程序中.

例如, 你可以创建一个实现了 IJob 接口的作业类 "SalesReportJob". 作业可能把发送给它的参数 (通过 JobDataMap) 用来指定销售人员的名称. 然后, 他们可以创建作业的多个定义 (JobDetails), 例如 “SalesReportForJoe” 和“SalesReportForMike”, 它们对应作业的输入参数有 JobDataMaps 中指定的 “joe” 和 “mike”.

当触发器触发时, 它所关联的 JobDetail (实例定义) 将被加载, 并且她引用的作业类将通过调度程序上配置的 JobFactory 来实例化. 默认的 JobFactory 只是使用 Activator.CreateInstance 调用作业类的默认构造函数, 然后尝试调用该类的 JobDataMap 中的匹配键名称的 setter 属性. 你可能希望创建自己的 JobFactory 实现来完成诸如让应用程序的 IoC 或 DI 容器生成/初始化作业实例等事情.

在 “Quartz speak” 中, 我们将每个存储的 JobDetail 称之为 “作业定义” 或 “作业详情实例”, 并将每个正在执行的作业称为 “作业实例” 或 “作业定义实例”. 通常, 如果我们只使用 “job” 这个词,我们就是指一个命名定义, 或者 JobDetail. 当我们指的是实现了作业接口的类时, 我们通常使用术语 “job type”.

作业状态和并发

一些关于作业状态数据 (也叫 JobDataMap) 和并发的附加说明. 有几个属性可以添加的你的作业类中从而影响 Quartz 相关的行为.

DisallowConcurrentExecution 是一个可以添加到作业类的属性她告诉 Quartz 不要同时执行指定的 Job 定义(指定的作业类)的多个实例. 注意这里的描述, 因为它是精心挑选的. 在上一节的示例中, 如果 "SalesReportJob" 有这个属性, 则在指定时间只能执行一个 "SalesReportForJoe" 实例, 但它可以与 "SalesReportForMike" 实例同时执行. 该约束基于实例定义 (JobDetail), 而不是基于作业类的实例. 然而, 她通常会对类的编码方式产生影响, 还是决定(在 Quartz 的设计过程中)将属性携带到作业类本身.

PersistJobDataAfterExecution 是一个可以添加到作业类的属性她告诉 Quartz 在 Execute()方法成功完成 (没有抛出异常) 之后更新 JobDetail 的 JobDataMap 的存储副本, 这样,同一作业 (JobDetail) 的下一次执行将接收到更新的值, 而不是原始存储的值. 与 DisallowConcurrentExecution 属性一样, 这适用于作业定义实例, 而不是作业类实例, 因为她会对类的编码方式产生影响 (例如, Execute() 方法中的代码需要显式地 '理解' '状态性'), 决定让作业类携带该属性.

如果你使用 PersistJobDataAfterExecution 属性, 则应着重考虑同时使用 DisallowConcurrentExecution 属性, 以规避当同一作业 (JobDetail) 的两个实例并发执行时可能的数据混淆 (竞态条件).

其他作业属性

下面是可以通过 JobDetail 对象为作业实例定义其他属性的快速摘要:

  • Durability - 如果一个作业是非持久性的, 那么当没有任何活动的触发器与之关联时, 该作业将被调度程序自动删除. 换句话说, 非持久性作业的寿命取决于其触发器的存在.
  • RequestsRecovery - 如果一个作业 "请求恢复", 并且其在调度器的 '硬关闭' 期间执行 (即: 所在进程崩溃, 或机器被关机), 那么当调度程序重启时该作业会重新执行. 在这种情况下, JobExecutionContext.Recovering 属性将返回 true.

作业执行异常

最后, 我们需要告知您 IJob.Execute(..) 方法的一些细节. 您应该从 Execute(..) 方法抛出的唯一异常类型是 JobExecutionException. 因此, 您通常应使用 'try-catch' 代码块包含 Execute(..) 方法的整个内容. 您还应该花一些时间看看 JobExecutionException 的文档, 因为您的作业可以使用她向调度程序提供有关如何处理异常的各种指令.

[译]课程 3: 更多关于 Jobs 和 JobsDetails的更多相关文章

  1. [译]Quartz.Net 框架 教程(中文版)2.2.x 之第三课 更多关于Jobs和JobDetails

    第三课 更多关于Jobs和JobDetails 在这二课我们已经学习到,Jobs接口非常容易实现,只有一个execute方法.我们需要再学习一些知识去理解jobs的本质,Job接口的execute方法 ...

  2. [译]课程 1: 使用 Quartz

    译者注: 原文在这 Lesson 1: Using Quartz 在你使用调度器之前, 你需要先实例化(能猜到是谁么?). 要实例化, 请使用 ISchedulerFactory 的实现. 译者注: ...

  3. [译]Quartz 框架 教程(中文版)2.2.x 之第二课 Quartz API,Jobs和Triggers简介

    第二课:QuartzAPI,Jobs和Triggers简介 Quartz API Quartz API 关键的几个接口: Scheduler:跟任务调度相关的最主要的API接口. Job:你期望任务调 ...

  4. [译]Quartz.NET 3.x 教程

    译者注: 最近有点小浮躁,找点事做做平静下内心的焦作,干脆翻译下 Quartz.NET 3.x Tutorial 好了. Quartz.NET 3.x 教程 选择课程:带划线的表示没完成 课程 1: ...

  5. 通过示例学习rholang(下部:课程8-13)

    课程8——状态通道和方法 保存数据 到现在为止,你已经很擅长于发送数据到元组空间和从元组空间中获取数据.但是无论你在什么时候进行计算,你有时需要把一些数据放在一边晚点才使用.几乎所有编程语言都有变量的 ...

  6. Quartz.NET syudy

    Quartz.NET   Quartz.NET是一个开源的作业调度框架,是OpenSymphony 的 Quartz API的.NET移植,它用C#写成,可用于winform和asp.net应用中.它 ...

  7. Quartz.net官方开发指南系列篇

    Quartz.NET是一个开源的作业调度框架,是OpenSymphony 的 Quartz API的.NET移植,它用C#写成,可用于winform和asp.net应用中.它提供了巨大的灵活性而不牺牲 ...

  8. (5)Quartz学习

    原文:http://blog.csdn.net/zxl315/article/details/10879927 介绍Quartz Quartz是一个开源的任务调度系统,它能用来调度很多任务的执行. 运 ...

  9. 初识 .NET平台下作业调度器——Quartz.NET

    Quartz.NET是一个开源的作业调度框架,是OpenSymphony 的 Quartz API的.NET移植,它用C#写成,可用于winform和asp.net应用中.它提供了巨大的灵活性而不牺牲 ...

随机推荐

  1. 【Java并发基础】并发编程bug源头:可见性、原子性和有序性

    前言 CPU .内存.I/O设备之间的速度差距十分大,为了提高CPU的利用率并且平衡它们的速度差异.计算机体系结构.操作系统和编译程序都做出了改进: CPU增加了缓存,用于平衡和内存之间的速度差异. ...

  2. MySQL 行列相互转换

    行列相互转换 /*创建表*/ CREATE TABLE ic ( NAME ), Product ), amount INT ); INSERT INTO ic VALUES (), (), (), ...

  3. django操作命令

    下载安装 pip3 install django==1.11.21 -i https://pypi.tuna.tsinghua.edu.cn/simple 创建项目 1.终端找到存放项目的文件夹,dj ...

  4. 2019CSP复赛游记

    Day 0 作为一个初三的小蒟蒻…… 什么算法都不会打…… 做一道LCA+生成树的图论题调了两个小时…… 明日裸考…… Day 1 Morning 买了两个士力架,带了一盒牛奶,准备在考场上食用(这个 ...

  5. Matplotlib从兴趣到实践

    先看下Matplotlib实现的效果 是不是出现了也想敲一个的心动,那让我们一起来了解Matplotlib吧 Matplotlib安装 1.Windows系统安装Matplotlib 进入到cmd的命 ...

  6. 替代 Hystrix,Spring Cloud Alibaba Sentinel 快速入门

    提起 Spring Cloud 的限流降级组件,一般首先想到的是 Netflix 的 Hystrix. 不过就在2018年底,Netflix 宣布不再积极开发 Hystrix,该项目将处于维护模式.官 ...

  7. MyBatis3——输出参数ResultType、动语态sql

    输出参数ResultType 1.输出参数为简单类型(8个基本+String) 2.输出参数为对象类型 3.输出参数为实体对象类型的集合:虽然输出类型为集合,但是resultType依然写集合的元素类 ...

  8. THUWC2020 自闭记

    DAY 1 报道 领胸牌和-围巾-! 发现我和 \(ssf\) 小姐姐一个考场. 合影+开幕式 宾馆睡了一觉-睡上午觉真的舒服. 合影时在c位! 开幕式.比上次夏令营不知道好到哪里去了,讲话都挺有意思 ...

  9. [hdu2255] 奔小康赚大钱

    Description 传说在遥远的地方有一个非常富裕的村落,有一天,村长决定进行制度改革:重新分配房子. 这可是一件大事,关系到人民的住房问题啊.村里共有 \(n\) 间房间,刚好有 \(n\) 家 ...

  10. 深入Node模块Buffer-学会操作二进制

    Buffer 作为 nodejs 中重要的概念和功能,为开发者提供了操作二进制的能力.本文记录了几个问题,来加深对 Buffer 的理解和使用: 认识缓冲器 如何申请堆外内存 如何计算字节长度 如何计 ...