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建表脚本:

Quartz.NET开源作业调度框架系列(五):AdoJobStore保存job到数据库_第1张图片

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 }

 Quartz.NET开源作业调度框架系列(五):AdoJobStore保存job到数据库_第2张图片

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

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

Quartz.NET开源作业调度框架系列(五):AdoJobStore保存job到数据库_第3张图片

你可能感兴趣的:(Quartz.NET开源作业调度框架系列(五):AdoJobStore保存job到数据库)