[译]课程 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应用中.它提供了巨大的灵活性而不牺牲 ...
随机推荐
- 从操作系统层面理解Linux下的网络IO模型
I/O( INPUT OUTPUT),包括文件I/O.网络I/O. 计算机世界里的速度鄙视: 内存读数据:纳秒级别. 千兆网卡读数据:微妙级别.1微秒=1000纳秒,网卡比内存慢了千倍. 磁盘读数据: ...
- python的list()函数
list()函数将其它序列转换为 列表 (就是js的数组). 该函数不会改变 其它序列 效果图一: 代码一: # 定义一个元组序列 tuple_one = (123,','abc') print( ...
- 从头学pytorch(二十):残差网络resnet
残差网络ResNet resnet是何凯明大神在2015年提出的.并且获得了当年的ImageNet比赛的冠军. 残差网络具有里程碑的意义,为以后的网络设计提出了一个新的思路. googlenet的思路 ...
- 【ARM】---关于ARM内核与架构的解释
本文摘自某论坛某位大神的一段回复,经典至极,copy来己用! 只要你玩过ARM内核的芯片,那么关于内核和架构,我想应该或多或少的困惑过你,看了下面的介绍,你应该会清楚很多! 好比你盖房子,刚开始因为水 ...
- 架构师JavaScript 的对象继承方式,有几种程序写法?
架构师JavaScript 的对象继承方式,有几种程序写法? 一.对象冒充 其原理如下:构造函数使用 this 关键字给所有属性和方法赋值(即采用类声明的构造函数方式).因为构造函数只是一个函数, ...
- java intellij 工具的简单用法
一.目录结构 1.新建项目(Empty Project) -> 新建module(可以有多个) => 出来src文件夹 -> 在src文件夹中新建package -> 在pa ...
- java 赋值运算
注意:在赋值运算的时候,会自动发生数据类型转变 例子 public class test{ public static void main(String[] args){ byte num = 5; ...
- Elasticsearch如何修改Mapping结构并实现业务零停机
Elasticsearch 版本:6.4.0 一.疑问 在项目中后期,如果想调整索引的 Mapping 结构,比如将 ik_smart 修改为 ik_max_word 或者 增加分片数量 等,但 El ...
- 巨坑练习题!!!—— Car的旅行线路
在看题目之前,请童鞋们做好心理准备╮(╯▽╰)╭ 题目描述 又到暑假了,住在城市A的Car想和朋友一起去城市B旅游. 她知道每个城市都有四个飞机场,分别位于一个矩形的四个顶点上,同一个城市中两个机场之 ...
- Lobooi 结对作业(24235+24229)
结队作业 GitHub项目地址 https://github.com/Lobooi/PairProgramming.git 伙伴博客地址 https://www.cnblogs.com/lanti/p ...