[译]课程 3: 更多关于 Jobs 和 JobsDetails
译者注:
目录在这 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的更多相关文章
- [译]Quartz.Net 框架 教程(中文版)2.2.x 之第三课 更多关于Jobs和JobDetails
第三课 更多关于Jobs和JobDetails 在这二课我们已经学习到,Jobs接口非常容易实现,只有一个execute方法.我们需要再学习一些知识去理解jobs的本质,Job接口的execute方法 ...
- [译]课程 1: 使用 Quartz
译者注: 原文在这 Lesson 1: Using Quartz 在你使用调度器之前, 你需要先实例化(能猜到是谁么?). 要实例化, 请使用 ISchedulerFactory 的实现. 译者注: ...
- [译]Quartz 框架 教程(中文版)2.2.x 之第二课 Quartz API,Jobs和Triggers简介
第二课:QuartzAPI,Jobs和Triggers简介 Quartz API Quartz API 关键的几个接口: Scheduler:跟任务调度相关的最主要的API接口. Job:你期望任务调 ...
- [译]Quartz.NET 3.x 教程
译者注: 最近有点小浮躁,找点事做做平静下内心的焦作,干脆翻译下 Quartz.NET 3.x Tutorial 好了. Quartz.NET 3.x 教程 选择课程:带划线的表示没完成 课程 1: ...
- 通过示例学习rholang(下部:课程8-13)
课程8——状态通道和方法 保存数据 到现在为止,你已经很擅长于发送数据到元组空间和从元组空间中获取数据.但是无论你在什么时候进行计算,你有时需要把一些数据放在一边晚点才使用.几乎所有编程语言都有变量的 ...
- Quartz.NET syudy
Quartz.NET Quartz.NET是一个开源的作业调度框架,是OpenSymphony 的 Quartz API的.NET移植,它用C#写成,可用于winform和asp.net应用中.它 ...
- Quartz.net官方开发指南系列篇
Quartz.NET是一个开源的作业调度框架,是OpenSymphony 的 Quartz API的.NET移植,它用C#写成,可用于winform和asp.net应用中.它提供了巨大的灵活性而不牺牲 ...
- (5)Quartz学习
原文:http://blog.csdn.net/zxl315/article/details/10879927 介绍Quartz Quartz是一个开源的任务调度系统,它能用来调度很多任务的执行. 运 ...
- 初识 .NET平台下作业调度器——Quartz.NET
Quartz.NET是一个开源的作业调度框架,是OpenSymphony 的 Quartz API的.NET移植,它用C#写成,可用于winform和asp.net应用中.它提供了巨大的灵活性而不牺牲 ...
随机推荐
- cogs 14. [网络流24题] 搭配飞行员 二分图最大匹配 匈牙利算法
14. [网络流24题] 搭配飞行员 ★★ 输入文件:flyer.in 输出文件:flyer.out 简单对比时间限制:1 s 内存限制:128 MB [问题描述] 飞行大队有 ...
- vue基础中的注意事项,以及一些学习心得
vue中你不知道的东西.以及注意事项 v-html 使用 v-html的时候该指令中的值会覆盖绑定标签中原有的值,且使用v-html的时候不要将他设置为给用户提供内容的地方,因为v-html很容易被X ...
- hdu6703 线段树+set
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6703 给你一个数组两种操作.操作一是将pos位置的数字加上10000000:操作二是给你个r和k,问你 ...
- CodeSign error: no provisioning profile at path '/Users/zhht-2015/Library/MobileDevice/Provisioning Profiles/79693141-f98b-4ac4-8bb4-476c9475f265.mobileprovision'
解决方法: 1.关闭Xcode,找到项目中的**.xcodeproj文件,点击右键,show package contents(打开包内容). 2.打开后找到project.pbxproj文件,用文本 ...
- DirectX11 Windows Windows SDK--28 计算着色器:波浪(水波)
前言 有关计算着色器的基础其实并不是很多.接下来继续讲解如何使用计算着色器实现水波效果,即龙书中所实现的水波.但是光看代码可是完全看不出来是在做什么的.个人根据书中所给的参考书籍找到了对应的实现原理, ...
- jade 的 考古
Jade是一款高性能简洁易懂的模板引擎(加上这两个字我想起了发动机,为什么不直接叫发动机呢), Jade是Haml的Javascript实现, 在服务端(NodeJS)及客户端均有支持. haml 是 ...
- Kindle Unlimited 上线的最热书单
Kindle 也给出了一份,到现在为止,在 Kindle Unlimited 上线的最热书单: 1.岛上书店2.一个人的朝圣3.自控力4.嫌疑人 X 的献身5.沉默的大多数(王小波文集)6.跟任何人都 ...
- Python3-提高效率的方法
1.字符串格式化 Python3.7或以上推荐使用f-string,其他版本推荐使用format方法. 2.字典的初始化 使用字面量初始化字典(以及其他集合类型). 说明:Python中初始化集合类型 ...
- ios--->泛型
泛型 开发中使用场景: 1.限制集合中的类型,只能检测方法的调用,因为声明的泛型只能存在方法中 2.当一个类在声明的时候,某个对象的属性不确定,只有创建对象的时候才能确定,就可以使用泛型. 使用泛型的 ...
- 集合详解之 Collection
集合详解之 Collection 先来看看集合的继承关系图,如下图所示: 其中: 外框为虚线的表示接口,边框为实线的表示类: 箭头为虚线的表示实现了接口,箭头为实线的表示继承了类. 为了方便理解,我隐 ...