Quartz. NET open-source job scheduling framework series (5): AdoJobStore stores jobs to databases,
Quartz. the core elements of NET job scheduling are schedgger, trigger, and job. trigger (the element used to define the scheduling time, that is, the time rule to execute the task) and job are the metadata of task scheduling, sched is the Controller that actually executes scheduling. There are two types of jobs in Quartz. NET: stateless and stateful ). For the same trigger, a stateful job cannot be executed in parallel. The next trigger can be triggered only after the last trigger task is executed. A stateless task is a concurrent task, that is, tasks are independent of each other and do not interfere with each other. A job can be associated with multiple triggers, but one trigger can be associated with only one job. Some tasks need to add, delete, modify, and process data in the database. If these tasks cannot be executed concurrently, stateless tasks are required. Otherwise, data confusion may occur.
In some cases, we need to save the task to the database. In particular, some tasks contain parameters, such as accumulated tasks. If they can be saved to the database, even if the intermediate power is down or the program restarts abnormally, the intermediate computing results will not be lost. You can perform operations from the breakpoint results (first resume the task ), the following describes how to use AdoJobStore to save tasks to the SQL Server database.
Create a new QRTZ _ database in the database and execute the SQL table creation script:
1 RecoveryJob
Is a stateless task, the Code is as follows:
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 QuartzDemo10 {11 /// <summary> 12 // stateless recoverable task 13 /// </summary> 14 public class RecoveryJob: IJob15 {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 try25 {26 // obtain the current Form1 instance 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 else34 {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 // whether it can be recovered from the database. If information such as the Job is saved, the program runs suddenly on the terminal (interrupted during debugging, instead of shutting down the form for simulation) 43 count = data. getInt (Count); 44} 45 else46 {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 // determine whether the form is opened 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
It is a stateful task. The difference between a stateful task and a stateless task is to use [PersistJobDataAfterExecution] on the task class to indicate that the task is stateful, and a stateful task cannot be executed concurrently, you also need to mark [DisallowConcurrentExecution]. The Code is as follows:
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 QuartzDemo10 {11 /// <summary> 12 // use this [PersistJobDataAfterExecution] to indicate that the task is stateful, 13 // a stateful task cannot be concurrently executed [DisallowConcurrentExecution] 14 /// </summary> 15 [PersistJobDataAfterExecution] 16 [DisallowConcurrentExecution] 17 public class RecoveryStatefulJob: recoveryJob18 {19 20} 21}
3 AdoJobStoreExample
Use properties ["quartz. dataSource. default. connectionString "] =" Server = (local); Database = QRTZ _; Trusted_Connection = True; "; defines the connection information of the Database. The task is automatically saved to the Database when the program is running:
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 usage example 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 // database connection string 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 whether bool B is restored = 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 // define a stateless task 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 // you can use this to view the trigger rule 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 // If a job or trigger with the same name already exists in the database, the binding fails to be 77 sched. scheduleJob (job, trigger); 78} 79 catch 80 {81 B restore = true; 82} 83 count ++; 84 85 // define a stateful task ****************************** * **************************** 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 catch102 {103 B restore = true; 104} 105 106 107} 108 109 110 // start 111 sched. start (); 112 // sched. shutdown (); 113 114} 115 116 public string Name117 {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}
We can see that the stateful count accumulates 1 each time, while the stateless count loses the accumulated number (new instance) Each execution, interrupts the program, and views the database's QRTZ_JOB_DETAILS table, we can see that there is another persistent task:
After the program is interrupted (the form is not closed during debugging, but debugging is interrupted and an exception is simulated), run the program again and you can see the following interface: