zoukankan      html  css  js  c++  java
  • 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/p/Quartz_NET_stateful_job_adojobstore_sql_server.html
  • 相关阅读:
    Java实现 LeetCode 50 Pow(x,n)
    Java实现 LeetCode 50 Pow(x,n)
    Java实现 LeetCode 49 字母异位词分组
    Java实现 LeetCode 49 字母异位词分组
    Java实现 LeetCode 49 字母异位词分组
    Java实现 LeetCode 48 旋转图像
    Java实现 LeetCode 48 旋转图像
    Java实现 LeetCode 48 旋转图像
    Java实现 LeetCode 47 全排列 II(二)
    Java实现 LeetCode 47 全排列 II(二)
  • 原文地址:https://www.cnblogs.com/tianciliangen/p/8385240.html
Copyright © 2011-2022 走看看