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表,可以看见还有一个持久化的任务:

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

  • 相关阅读:
    JMeter网站测试分析
    JMeter元件的作用域和执行顺序
    JMeter脚本录制
    认识Jmeter工具
    Fiddler 只取所需
    Fiddler设置代理(PC和Android)
    Fiddler获取https会话
    Fiddler 你需要了解的
    关于excel的导入导出
    第十章、random模块
  • 原文地址:https://www.cnblogs.com/isaboy/p/Quartz_NET_stateful_job_adojobstore_sql_server.html
Copyright © 2011-2022 走看看