Quartz.NET开源作业调度框架系列(五):AdoJobStore保存job到数据库
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
是一个无状态的任务,代码如下:
1 using System;
2 using System.Collections.Specialized;
3 using System.Threading;
4 using Common.Logging;
5 using Quartz;
6 using Quartz.Impl;
7 using Quartz.Job;
8 using System.Windows.Forms;
9 namespace QuartzDemo
10 {
11 /// <summary>
12 /// 无状态的可恢复的任务
13 /// </summary>
14 public class RecoveryJob : IJob
15 {
16
17 private const string Count = "count";
18 public virtual void Execute(IJobExecutionContext context)
19 {
20
21 JobKey jobKey = context.JobDetail.Key;
22 if (isOpen("FrmConsole"))
23 {
24 try
25 {
26 //获取当前Form1实例
27 __instance = (FrmConsole)Application.OpenForms["FrmConsole"];
28 // 如果任务是恢复的任务的话
29 if (context.Recovering)
30 {
31 __instance.SetInfo(string.Format("{0} RECOVERING at {1}", jobKey, DateTime.Now.ToString("r")));
32 }
33 else
34 {
35 __instance.SetInfo(string.Format("{0} starting at {1}", jobKey, DateTime.Now.ToString("r")));
36 }
37
38 JobDataMap data = context.JobDetail.JobDataMap;
39 int count;
40 if (data.ContainsKey(Count))
41 {
42 //是否能从数据库中恢复,如果保存Job等信息的话,程序运行突然终端(可用调试时中断运行,而不是关闭窗体来模拟)
43 count = data.GetInt(Count);
44 }
45 else
46 {
47 count = 0;
48 }
49 count++;
50 data.Put(Count, count);
51
52 __instance.SetInfo(string.Format(" {0} Count #{1}", jobKey, count));
53 }
54 catch (Exception ex)
55 {
56 Console.WriteLine(ex.Message);
57 }
58 }
59 }
60
61
62 private static FrmConsole __instance = null;
63
64 /// <summary>
65 /// 判断窗体是否打开
66 /// </summary>
67 /// <param name="appName"></param>
68 /// <returns></returns>
69 private bool isOpen(string appName)
70 {
71 FormCollection collection = Application.OpenForms;
72 foreach (Form form in collection)
73 {
74 if (form.Name == appName)
75 {
76 return true;
77 }
78 }
79 return false;
80 }
81
82 }
83 }
2 RecoveryStatefulJob
是一个有状态的任务,和无状态的区别就是在任务类的上面用[PersistJobDataAfterExecution]标注任务是有状态的 , 有状态的任务不允许并发执行,也需要标注 [DisallowConcurrentExecution],代码如下:
1 using System;
2 using System.Collections.Specialized;
3 using System.Threading;
4 using Common.Logging;
5 using Quartz;
6 using Quartz.Impl;
7 using Quartz.Job;
8 using System.Windows.Forms;
9 namespace QuartzDemo
10 {
11 /// <summary>
12 /// 用这个[PersistJobDataAfterExecution]标注任务是有状态的,
13 /// 有状态的任务不允许并发执行 [DisallowConcurrentExecution]
14 /// </summary>
15 [PersistJobDataAfterExecution]
16 [DisallowConcurrentExecution]
17 public class RecoveryStatefulJob : RecoveryJob
18 {
19
20 }
21 }
3 AdoJobStoreExample
用 properties["quartz.dataSource.default.connectionString"] = "Server=(local);Database=QRTZ_;Trusted_Connection=True;";定义了数据库的连接信息,程序运行时会自动将任务保存到数据库中:
1 using System;
2 using System.Collections.Specialized;
3 using System.Threading;
4 using Common.Logging;
5 using Quartz;
6 using Quartz.Impl;
7 using Quartz.Job;
8 using System.Windows.Forms;
9 namespace QuartzDemo
10 {
11 /// <summary>
12 /// AdoJobStore的用法示例
13 /// </summary>
14 public class AdoJobStoreExample
15 {
16 public virtual void Run(bool inClearJobs, bool inScheduleJobs)
17 {
18 NameValueCollection properties = new NameValueCollection();
19
20 properties["quartz.scheduler.instanceName"] = "TestScheduler";
21 properties["quartz.scheduler.instanceId"] = "instance_one";
22 properties["quartz.threadPool.type"] = "Quartz.Simpl.SimpleThreadPool, Quartz";
23 properties["quartz.threadPool.threadCount"] = "5";
24 properties["quartz.threadPool.threadPriority"] = "Normal";
25 properties["quartz.jobStore.misfireThreshold"] = "60000";
26 properties["quartz.jobStore.type"] = "Quartz.Impl.AdoJobStore.JobStoreTX, Quartz";
27 properties["quartz.jobStore.useProperties"] = "false";
28 properties["quartz.jobStore.dataSource"] = "default";
29 properties["quartz.jobStore.tablePrefix"] = "QRTZ_";
30 properties["quartz.jobStore.clustered"] = "true";
31 // SQLite
32 // properties["quartz.jobStore.lockHandler.type"] = "Quartz.Impl.AdoJobStore.UpdateLockRowSemaphore, Quartz";
33 properties["quartz.jobStore.driverDelegateType"] = "Quartz.Impl.AdoJobStore.SqlServerDelegate, Quartz";
34 // 数据库连接字符串
35 properties["quartz.dataSource.default.connectionString"] = "Server=(local);Database=QRTZ_;Trusted_Connection=True;";
36 properties["quartz.dataSource.default.provider"] = "SqlServer-20";
37
38 // First we must get a reference to a scheduler
39 ISchedulerFactory sf = new StdSchedulerFactory(properties);
40 IScheduler sched = sf.GetScheduler();
41
42 bool b是否恢复 = false;
43 if (inClearJobs)
44 {
45 Console.WriteLine("***** Deleting existing jobs/triggers *****");
46 // sched.Clear();
47 }
48
49
50 if (inScheduleJobs)
51 {
52
53 string schedId = sched.SchedulerInstanceId;
54
55 int count = 1;
56
57 //定义一个无状态的任务
58 IJobDetail job = JobBuilder.Create<RecoveryJob>()
59 .WithIdentity("recoveryjob_" + count, schedId)
60 .RequestRecovery() //recovery
61 .Build();
62
63
64 ISimpleTrigger trigger = (ISimpleTrigger)TriggerBuilder.Create()
65 .WithIdentity("triger_" + count, schedId)
66 .StartAt(DateBuilder.FutureDate(1, IntervalUnit.Second))
67 .WithSimpleSchedule(x => x.WithRepeatCount(20).WithInterval(TimeSpan.FromSeconds(3)))
68 .Build();
69 //可用此来查看定义的触发器触发规则
70 //log.InfoFormat("{0} will run at: {1} and repeat: {2} times, every {3} seconds",
71 //job.Key, trigger.GetNextFireTimeUtc(),
72 //trigger.RepeatCount,
73 //trigger.RepeatInterval.TotalSeconds);
74 try
75 {
76 //如果数据库已经存在同名job和trigger,则绑定失败
77 sched.ScheduleJob(job, trigger);
78 }
79 catch
80 {
81 b是否恢复 = true;
82 }
83 count++;
84
85 //定义一个有状态的任务***********************************************************
86 job = JobBuilder.Create<RecoveryStatefulJob>()
87 .WithIdentity("Statefuljob_" + count, schedId)
88 .RequestRecovery() // recovery
89 .Build();
90
91 trigger = (ISimpleTrigger)TriggerBuilder.Create()
92 .WithIdentity("triger_" + count, schedId)
93 .StartAt(DateBuilder.FutureDate(1, IntervalUnit.Second))
94 .WithSimpleSchedule(x => x.WithRepeatCount(20).WithInterval(TimeSpan.FromSeconds(3)))
95 .Build();
96
97 try
98 {
99 sched.ScheduleJob(job, trigger);
100 }
101 catch
102 {
103 b是否恢复 = true;
104 }
105
106
107
108 }
109
110 //启动
111 sched.Start();
112 //sched.Shutdown();
113
114 }
115
116 public string Name
117 {
118 get { return GetType().Name; }
119 }
120
121 public void Run()
122 {
123 bool clearJobs = true;
124 //clearJobs = false;
125 bool scheduleJobs = true;
126 AdoJobStoreExample example = new AdoJobStoreExample();
127 example.Run(clearJobs, scheduleJobs);
128 }
129 }
130 }
可以看到有状态的计数每次累加1,而无状态的每次执行时都会丢失累加数(新的实例),中断程序,查看数据库的QRTZ_JOB_DETAILS表,可以看见还有一个持久化的任务:
中断程序后(调试状态时不关闭窗体,而是中断调试,模拟异常关闭) ,再重新运行可以看到如下界面:
出处:http://www.cnblogs.com/isaboy/
声明:本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。