Quartz.NET 任务调度的核心元素是 scheduler, trigger 和 job,其中 trigger(用于定义调度时间的元素,即按照什么时间规则去执行任务) 和 job 是任务调度的元数据,scheduler 是实际执行调度的控制器。在Quartz.NET中主要有两种类型的 job:无状态的(stateless)和有状态的(stateful)。对于同一个 trigger 来说,有状态的 job 不能被并行执行,只有上一次触发的任务被执行完之后,才能触发下一次执行。无状态任务一般指可以并发的任务,即任务之间是独立的,不会互相干扰。一个 job 可以被多个 trigger 关联,但是一个 trigger 只能关联一个 job。某些任务需要对数据库中的数据进行增删改处理 , 这些任务不能并发执行,就需要用到无状态的任务 , 否则会造成数据混乱。

  另外有些情况下,我们需要将任务保存到数据库中,特别是有些任务中包含参数,例如累加的任务,如果可以保存到数据库中,即便中间断电或者程序异常重启,中间计算的结果也不会丢失,可以从断点的结果进行运算(首先恢复任务),下面介绍一下如何用AdoJobStore将任务保存到SQL Server数据库中.

  事先要在数据库上新建一个QRTZ_数据库,并执行SQL建表脚本:

1 RecoveryJob

  是一个无状态的任务,代码如下:

 using System;
using System.Collections.Specialized;
using System.Threading;
using Common.Logging;
using Quartz;
using Quartz.Impl;
using Quartz.Job;
using System.Windows.Forms;
namespace QuartzDemo
{
/// <summary>
/// 无状态的可恢复的任务
/// </summary>
public class RecoveryJob : IJob
{ private const string Count = "count";
public virtual void Execute(IJobExecutionContext context)
{ JobKey jobKey = context.JobDetail.Key;
if (isOpen("FrmConsole"))
{
try
{
//获取当前Form1实例
__instance = (FrmConsole)Application.OpenForms["FrmConsole"];
// 如果任务是恢复的任务的话
if (context.Recovering)
{
__instance.SetInfo(string.Format("{0} RECOVERING at {1}", jobKey, DateTime.Now.ToString("r")));
}
else
{
__instance.SetInfo(string.Format("{0} starting at {1}", jobKey, DateTime.Now.ToString("r")));
} JobDataMap data = context.JobDetail.JobDataMap;
int count;
if (data.ContainsKey(Count))
{
//是否能从数据库中恢复,如果保存Job等信息的话,程序运行突然终端(可用调试时中断运行,而不是关闭窗体来模拟)
count = data.GetInt(Count);
}
else
{
count = ;
}
count++;
data.Put(Count, count); __instance.SetInfo(string.Format(" {0} Count #{1}", jobKey, count));
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
} private static FrmConsole __instance = null; /// <summary>
/// 判断窗体是否打开
/// </summary>
/// <param name="appName"></param>
/// <returns></returns>
private bool isOpen(string appName)
{
FormCollection collection = Application.OpenForms;
foreach (Form form in collection)
{
if (form.Name == appName)
{
return true;
}
}
return false;
} }
}

2 RecoveryStatefulJob

  是一个有状态的任务,和无状态的区别就是在任务类的上面用[PersistJobDataAfterExecution]标注任务是有状态的 , 有状态的任务不允许并发执行,也需要标注 [DisallowConcurrentExecution],代码如下:

 using System;
using System.Collections.Specialized;
using System.Threading;
using Common.Logging;
using Quartz;
using Quartz.Impl;
using Quartz.Job;
using System.Windows.Forms;
namespace QuartzDemo
{
/// <summary>
/// 用这个[PersistJobDataAfterExecution]标注任务是有状态的,
/// 有状态的任务不允许并发执行 [DisallowConcurrentExecution]
/// </summary>
[PersistJobDataAfterExecution]
[DisallowConcurrentExecution]
public class RecoveryStatefulJob : RecoveryJob
{ }
}

3 AdoJobStoreExample

  用 properties["quartz.dataSource.default.connectionString"] = "Server=(local);Database=QRTZ_;Trusted_Connection=True;";定义了数据库的连接信息,程序运行时会自动将任务保存到数据库中:

 using System;
using System.Collections.Specialized;
using System.Threading;
using Common.Logging;
using Quartz;
using Quartz.Impl;
using Quartz.Job;
using System.Windows.Forms;
namespace QuartzDemo
{
/// <summary>
/// AdoJobStore的用法示例
/// </summary>
public class AdoJobStoreExample
{
public virtual void Run(bool inClearJobs, bool inScheduleJobs)
{
NameValueCollection properties = new NameValueCollection(); properties["quartz.scheduler.instanceName"] = "TestScheduler";
properties["quartz.scheduler.instanceId"] = "instance_one";
properties["quartz.threadPool.type"] = "Quartz.Simpl.SimpleThreadPool, Quartz";
properties["quartz.threadPool.threadCount"] = "";
properties["quartz.threadPool.threadPriority"] = "Normal";
properties["quartz.jobStore.misfireThreshold"] = "";
properties["quartz.jobStore.type"] = "Quartz.Impl.AdoJobStore.JobStoreTX, Quartz";
properties["quartz.jobStore.useProperties"] = "false";
properties["quartz.jobStore.dataSource"] = "default";
properties["quartz.jobStore.tablePrefix"] = "QRTZ_";
properties["quartz.jobStore.clustered"] = "true";
// SQLite
// properties["quartz.jobStore.lockHandler.type"] = "Quartz.Impl.AdoJobStore.UpdateLockRowSemaphore, Quartz";
properties["quartz.jobStore.driverDelegateType"] = "Quartz.Impl.AdoJobStore.SqlServerDelegate, Quartz";
    // 数据库连接字符串
properties["quartz.dataSource.default.connectionString"] = "Server=(local);Database=QRTZ_;Trusted_Connection=True;";
properties["quartz.dataSource.default.provider"] = "SqlServer-20"; // First we must get a reference to a scheduler
ISchedulerFactory sf = new StdSchedulerFactory(properties);
IScheduler sched = sf.GetScheduler(); bool b是否恢复 = false;
if (inClearJobs)
{
Console.WriteLine("***** Deleting existing jobs/triggers *****");
// sched.Clear();
} if (inScheduleJobs)
{ string schedId = sched.SchedulerInstanceId; int count = ; //定义一个无状态的任务
IJobDetail job = JobBuilder.Create<RecoveryJob>()
.WithIdentity("recoveryjob_" + count, schedId)
.RequestRecovery() //recovery
.Build(); ISimpleTrigger trigger = (ISimpleTrigger)TriggerBuilder.Create()
.WithIdentity("triger_" + count, schedId)
.StartAt(DateBuilder.FutureDate(, IntervalUnit.Second))
.WithSimpleSchedule(x => x.WithRepeatCount().WithInterval(TimeSpan.FromSeconds()))
.Build();
//可用此来查看定义的触发器触发规则
//log.InfoFormat("{0} will run at: {1} and repeat: {2} times, every {3} seconds",
//job.Key, trigger.GetNextFireTimeUtc(),
//trigger.RepeatCount,
//trigger.RepeatInterval.TotalSeconds);
try
{
//如果数据库已经存在同名job和trigger,则绑定失败
sched.ScheduleJob(job, trigger);
}
catch
{
b是否恢复 = true;
}
count++; //定义一个有状态的任务***********************************************************
job = JobBuilder.Create<RecoveryStatefulJob>()
.WithIdentity("Statefuljob_" + count, schedId)
.RequestRecovery() // recovery
.Build(); trigger = (ISimpleTrigger)TriggerBuilder.Create()
.WithIdentity("triger_" + count, schedId)
.StartAt(DateBuilder.FutureDate(, IntervalUnit.Second))
.WithSimpleSchedule(x => x.WithRepeatCount().WithInterval(TimeSpan.FromSeconds()))
.Build(); try
{
sched.ScheduleJob(job, trigger);
}
catch
{
b是否恢复 = true;
} } //启动
sched.Start();
//sched.Shutdown(); } public string Name
{
get { return GetType().Name; }
} public void Run()
{
bool clearJobs = true;
//clearJobs = false;
bool scheduleJobs = true;
AdoJobStoreExample example = new AdoJobStoreExample();
example.Run(clearJobs, scheduleJobs);
}
}
}

  可以看到有状态的计数每次累加1,而无状态的每次执行时都会丢失累加数(新的实例),中断程序,查看数据库的QRTZ_JOB_DETAILS表,可以看见还有一个持久化的任务:

  中断程序后(调试状态时不关闭窗体,而是中断调试,模拟异常关闭) ,再重新运行可以看到如下界面:

Quartz.NET开源作业调度框架系列(五):AdoJobStore保存job到数据库的更多相关文章

  1. Quartz.NET开源作业调度框架系列

    Quartz.NET是一个被广泛使用的开源作业调度框架 , 由于是用C#语言创建,可方便的用于winform和asp.net应用程序中.Quartz.NET提供了巨大的灵活性但又兼具简单性.开发人员可 ...

  2. Quartz.NET开源作业调度框架系列(三):IJobExecutionContext 参数传递

    前面写了关于Quartz.NET开源作业调度框架的入门和Cron Trigger , 这次继续这个系列, 这次想讨论一下Quartz.NET中的Job如何通过执行上下文(Execution Conte ...

  3. Quartz.NET开源作业调度框架系列(三):IJobExecutionContext 参数传递-转

    前面写了关于Quartz.NET开源作业调度框架的入门和Cron Trigger , 这次继续这个系列, 这次想讨论一下Quartz.NET中的Job如何通过执行上下文(Execution Conte ...

  4. Quartz.NET开源作业调度框架系列(一):快速入门step by step

    Quartz.NET是一个被广泛使用的开源作业调度框架 , 由于是用C#语言创建,可方便的用于winform和asp.net应用程序中.Quartz.NET提供了巨大的灵活性但又兼具简单性.开发人员可 ...

  5. Quartz.NET开源作业调度框架系列(一):快速入门step by step-转

    Quartz.NET是一个被广泛使用的开源作业调度框架 , 由于是用C#语言创建,可方便的用于winform和asp.net应用程序中.Quartz.NET提供了巨大的灵活性但又兼具简单性.开发人员可 ...

  6. Quartz.NET开源作业调度框架系列(四):Plugin Job

    如果在Quartz.NET作业运行时我们想动态修改Job和Trigger的绑定关系,同时修改一些参数那么该怎么办呢?Quartz.NET提供了插件技术,可以通过在XML文件中对Job和Trigger的 ...

  7. Quartz.NET开源作业调度框架系列(四):Plugin Job-转

    如果在Quartz.NET作业运行时我们想动态修改Job和Trigger的绑定关系,同时修改一些参数那么该怎么办呢?Quartz.NET提供了插件技术,可以通过在XML文件中对Job和Trigger的 ...

  8. Quartz.NET开源作业调度框架系列(二):CronTrigger

    CronTriggers比SimpleTrigger更加的灵活和有用,对于比较复杂的任务触发规则,例如"每个星期天的晚上12:00"进行备份任务,SimpleTrigger就不能胜 ...

  9. Quartz.NET开源作业调度框架系列(二):CronTrigger-转

    CronTriggers比SimpleTrigger更加的灵活和有用,对于比较复杂的任务触发规则,例如"每个星期天的晚上12:00"进行备份任务,SimpleTrigger就不能胜 ...

随机推荐

  1. 快速入门系列--MVC--05行为

    Action执行包含内容比较多,主要有同步/异步Action的概念和执行过程,Authorationfilter, ActionFiltor, ResultFilter, ExceptionFilte ...

  2. struts2 OGNL表达式

    一.OGNL OGNL是Object-Graph Navigation Language的缩写,全称为对象图导航语言,是一种功能强大的表达式语言,它通过简单一致的语法,可以任意存取对象的属性或者调用对 ...

  3. Testing - 测试基础 - 模型

    珠玉在前,不再赘言. 软件测试模型 软件测试模型汇总

  4. 使用 CSS3 伪元素实现立体的照片堆叠效

    CSS3 里引入的伪元素让 Web 开发人员能够在不需要额外添加 HTML 标签的情况下制作出复杂的视觉效果.例如,:before 和 :after 这个两个 CSS3 伪元素就可以帮助你实现很多有趣 ...

  5. iOS_UIImage_Gif的分解

    /** Gif的步骤 1. 拿到Gifd的数据 2. 将Gif分解为一帧帧 3. 将单帧数据转为UIImage 4. 单帧图片保存 */ github地址: https://github.com/ma ...

  6. android gridview几个重要属性(android:listSelector自带内部padding分析)

    一.android:scrollbarStyle  决定状态条的位置     常用属性outsideOverlay,滚动条在最外层,gridview设置的padding在滚动条的内侧   二.andr ...

  7. [New Portal]Windows Azure Virtual Machine (15) 在本地制作数据文件VHD并上传至Azure(2)

    <Windows Azure Platform 系列文章目录> 在上一章内容里,我们已经将包含有OFFICE2013 ISO安装文件的VHD上传至Azure Blob Storage中了. ...

  8. 安装logstash,elasticsearch,kibana三件套

    logstash,elasticsearch,kibana三件套 elk是指logstash,elasticsearch,kibana三件套,这三件套可以组成日志分析和监控工具 注意: 关于安装文档, ...

  9. PyQt写的五子棋

    技术路线 GUI的实现 使用PyQt技术作为基础.PyQt是一个支持多平台的客户端开发SDK,使用它实现的客户端可以运行在目前几乎所有主流平台之上. 使用PyQt,Qt设计器实现UI,通过pyuic4 ...

  10. block,inline,inline-block的区别

    block: 英语翻译过来是“块”意思,就跟小时候玩过的积木方块一样,一块一块往上搭. inline: 英语翻译过来就是“内联”的意思,内联不好理解,我的理解就是行内元素: block和inline都 ...