Quartz.Net 学习之三

清单 3 显示了 CronTrigger 的一个示例。请注意 SchedulerFactory、Scheduler 和 JobDetail 的实例化,与 SimpleTrigger 示例中的实例化是相同的。在这个示例中,只是修改了触发器。这里指定的 cron 表达式(“0/5 * * * * ?”)安排任务每 5 秒执行一次。

清单3 CronTriggerRunner.cs

代码
   
     
1 using System;
2   using System.Collections.Generic;
3   using System.Text;
4   using Common.Logging;
5   using Quartz;
6   using Quartz.Impl;
7 using System.Threading;
8
9 namespace QuartzBeginnerExample
10 {
11 public class CronTriggerRunner : IExample
12 {
13 public string Name
14 {
15 get { return GetType().Name; }
16 }
17
18 public virtual void Run()
19 {
20 ILog log = LogManager.GetLogger( typeof (CronTriggerRunner));
21
22 log.Info( " ------- Initializing ------------------- " );
23
24 // First we must get a reference to a scheduler
25 ISchedulerFactory sf = new StdSchedulerFactory();
26 IScheduler sched = sf.GetScheduler();
27
28 log.Info( " ------- Initialization Complete -------- " );
29
30 log.Info( " ------- Scheduling Jobs ---------------- " );
31
32 // jobs can be scheduled before sched.start() has been called
33
34 // job 1 will run every 20 seconds
35 JobDetail job = new JobDetail( " job1 " , " group1 " , typeof (SimpleQuartzJob));
36 CronTrigger trigger = new CronTrigger( " trigger1 " , " group1 " , " job1 " , " group1 " );
37 trigger.CronExpressionString = " 0/20 * * * * ? " ;
38 sched.AddJob(job, true );
39 DateTime ft = sched.ScheduleJob(trigger);
40
41 log.Info( string .Format( " {0} has been scheduled to run at: {1} and repeat based on expression: {2} " , job.FullName, ft.ToString( " r " ), trigger.CronExpressionString));
42
43 log.Info( " ------- Starting Scheduler ---------------- " );
44
45 // All of the jobs have been added to the scheduler, but none of the
46 // jobs
47 // will run until the scheduler has been started
48 sched.Start();
49
50 log.Info( " ------- Started Scheduler ----------------- " );
51
52 log.Info( " ------- Waiting five minutes... ------------ " );
53 try
54 {
55 // wait five minutes to show jobs
56 Thread.Sleep( 300 * 1000 );
57 // executing...
58 }
59 catch (ThreadInterruptedException)
60 {
61 }
62
63 log.Info( " ------- Shutting Down --------------------- " );
64
65 sched.Shutdown( true );
66
67 log.Info( " ------- Shutdown Complete ----------------- " );
68
69 SchedulerMetaData metaData = sched.GetMetaData();
70 log.Info( string .Format( " Executed {0} jobs. " , metaData.NumJobsExecuted));
71 }
72
73 }
74 }
75

如上所示,只用作业和触发器,就能访问大量的功能。但是,Quartz 是个丰富而灵活的调度包,对于愿意研究它的人来说,它还提供了更多功能。下面看一下 Quartz 的一些高级特性。

 

作业管理和存储

作业一旦被调度,调度器需要记住并且跟踪作业和它们的执行次数。如果你的作业是30分钟后或每30秒调用,这不是很有用。事实上,作业执行需要非常准确和即时调用在被调度作业上的Execute()方法。Quartz通过一个称之为作业存储(JobStore)的概念来做作业存储和管理。

有效作业存储

Quartz提供两种基本作业存储类型。第一种类型叫做RAMJobStore,它利用通常的内存来持久化调度程序信息。这种作业存储类型最容易配置、构造和运行。Quartz.net缺省使用的就是RAMJobStore。对许多应用来说,这种作业存储已经足够了。

然而,因为调度程序信息是存储在被分配在内存里面,所以,当应用程序停止运行时,所有调度信息将被丢失。如果你需要在重新启动之间持久化调度信息,则将需要第二种类型的作业存储。为了修正这个问题,Quartz.NET 提供了 AdoJobStore。顾名思义,作业仓库通过 ADO.NET把所有数据放在数据库中。数据持久性的代价就是性能降低和复杂性的提高。它将所有的数据通过ADO.NET保存到数据库可中。它的配置要比 RAMJobStore稍微复杂,同时速度也没有那么快。但是性能的缺陷不是非常差,尤其是如果你在数据库表的主键上建立索引。

设置AdoJobStore

AdoJobStore 几乎可以在任何数据库上工作,它广泛地使用Oracle, MySQL, MS SQLServer2000, HSQLDB, PostreSQL 以及 DB2。要使用AdoJobStore,首先必须创建一套Quartz使用的数据库表,可以在Quartz 的database\tables找到创建库表的SQL脚本。如果没有找到你的数据库类型的脚本,那么找到一个已有的,修改成为你数据库所需要的。需要注意的一件事情就是所有Quartz库表名都以QRTZ_作为前缀(例如:表"QRTZ_TRIGGERS",及"QRTZ_JOB_DETAIL")。实际上,可以你可以将前缀设置为任何你想要的前缀,只要你告诉AdoJobStore那个前缀是什么即可(在你的Quartz属性文件中配置)。对于一个数据库中使用多个scheduler实例,那么配置不同的前缀可以创建多套库表,十分有用。

一旦数据库表已经创建,在配置和启动 AdoJobStore之前,就需要作出一个更加重要的决策。你要决定在你的应用中需要什么类型的事务。如果不想将scheduling命令绑到其他的事务上,那么你可以通过对JobStore使用JobStoreTX来让Quartz帮你管理事务(这是最普遍的选择)。

最后的疑问就是如何建立获得数据库联接的数据源(DataSource)。Quartz属性中定义数据源是通过提供所有联接数据库的信息,让Quartz自己创建和管理数据源。

要使用AdoJobStore(假定使用StdSchedulerFactory),首先需要设置Quartz配置中的quartz.jobStore.type属性为Quartz.Impl.AdoJobStore.JobStoreTX, Quartz。

配置 Quartz使用 JobStoreTx

quartz.threadPool.type = Quartz.Simpl.SimpleThreadPool, Quartz

下一步,需要为JobStore 选择一个DriverDelegate , DriverDelegate负责做指定数据库的所有ADO.NET工作。StdADO.NETDelegate是一个使用vanilla" ADO.NET代码(以及SQL语句)来完成工作的代理。如果数据库没有其他指定的代理,那么就试用这个代理。只有当使用 StdADO.NETDelegate发生问题时,我们才会使用数据库特定的代理(这看起来非常乐观。其他的代理可以在 Quartz.Impl.AdoJobStor命名空间找到。)。其他的代理包括PostgreSQLDelegate ( 专为PostgreSQL 7.x)。

一旦选择好了代理,就将它的名字设置给AdoJobStore。

配置AdoJobStore 使用DriverDelegate

quartz.threadPool.type = Quartz.Simpl.SimpleThreadPool, Quartz

接下来,需要为JobStore指定所使用的数据库表前缀(前面讨论过)。

配置AdoJobStore的数据库表前缀

quartz.jobStore.tablePrefix = QRTZ

然后需要设置JobStore所使用的数据源。必须在Quartz属性中定义已命名的数据源,比如,我们指定Quartz使用名为"default"的数据源(在配置文件的其他地方定义)。

配置 AdoJobStore使用数据源源的名字

properties["quartz.jobStore.dataSource"] = "default"

最后,需要配置数据源的使用的Ado.net数据提供者和数据库连接串,数据库连接串是标准的Ado.net 数据库连接的连接串。数据库提供者是关系数据库同Quartz.net之间保持低耦合的数据库的连接提供者.

配置AdoJobStore使用数据源源的数据库连接串和数据库提供者

quartz.dataSource.default.connectionString = Server=(local);Database=quartz;Trusted_Connection=True;

quartz.dataSource.default.provider= SqlServer-11

目前Quartz.net支持的以下数据库的数据提供者:

l SqlServer-11 - SQL Server driver for .NET Framework 1.1

l SqlServer-20 - SQL Server driver for .NET Framework 2.0

l OracleClient-20 - Microsoft''s Oracle Driver (comes bundled with .NET Framework)

l OracleODP-20 - Oracle''s Oracle Driver

l MySql-10 - MySQL Connector/.NET v. 1.0.7

l MySql-109 - MySQL Connector/.NET v. 1.0.9

l MySql-50 - MySQL Connector/.NET v. 5.0 (.NET 2.0)

l MySql-51 - MySQL Connector/:NET v. 5.1 (.NET 2.0)

l SQLite1044 - SQLite ADO.NET 2.0 Provider v. 1.0.44 (.NET 2.0)

如果Scheduler非常忙(比如,执行的任务数量差不多和线程池的数量相同,那么你需要正确地配置DataSource的连接数量为线程池数量。为了指示AdoJobStore所有的JobDataMaps中的值都是字符串,并且能以“名字-值”对的方式存储而不是以复杂对象的序列化形式存储在BLOB 字段中,应设置 quartz.jobStore.usePropertiess配置参数的值为"true"(这是缺省的方式)。这样做,从长远来看非常安全,这样避免了对存储在BLOB中的非字符串的序列化对象的类型转换问题。

清单 4 展示了 AdoJobStore提供的数据持久性。就像在前面的示例中一样,先从初始化 SchedulerFactory 和 Scheduler 开始。然后,不再需要初始化作业和触发器,而是要获取触发器群组名称列表,之后对于每个群组名称,获取触发器名称列表。请注意,每个现有的作业都应当用 Scheduler. RescheduleJob () 方法重新调度。仅仅重新初始化在先前的应用程序运行时终止的作业,不会正确地装载触发器的属性。

清单4 AdoJobStoreRunner.cs

 

代码
   
     
1 using System;
2 using System.Collections.Generic;
3 using System.Text;
4 using System.Collections.Specialized;
5 using Quartz;
6 using Common.Logging;
7 using Quartz.Impl;
8 using System.Threading;
9
10 namespace QuartzBeginnerExample
11 {
12 public class AdoJobStoreRunner : IExample
13 {
14 public string Name
15 {
16 get { return GetType().Name; }
17 }
18
19 private static ILog _log = LogManager.GetLogger( typeof (AdoJobStoreRunner));
20
21 public virtual void CleanUp(IScheduler inScheduler)
22 {
23 _log.Warn( " ***** Deleting existing jobs/triggers ***** " );
24
25 // unschedule jobs
26 string [] groups = inScheduler.TriggerGroupNames;
27 for ( int i = 0 ; i < groups.Length; i ++ )
28 {
29 String[] names = inScheduler.GetTriggerNames(groups[i]);
30 for ( int j = 0 ; j < names.Length; j ++ )
31 inScheduler.UnscheduleJob(names[j], groups[i]);
32 }
33
34 // delete jobs
35 groups = inScheduler.JobGroupNames;
36 for ( int i = 0 ; i < groups.Length; i ++ )
37 {
38 String[] names = inScheduler.GetJobNames(groups[i]);
39 for ( int j = 0 ; j < names.Length; j ++ )
40 inScheduler.DeleteJob(names[j], groups[i]);
41 }
42 }
43
44 public virtual void Run( bool inClearJobs, bool inScheduleJobs)
45 {
46 NameValueCollection properties = new NameValueCollection();
47
48 properties[ " quartz.scheduler.instanceName " ] = " TestScheduler " ;
49 properties[ " quartz.scheduler.instanceId " ] = " instance_one " ;
50 properties[ " quartz.threadPool.type " ] = " Quartz.Simpl.SimpleThreadPool, Quartz " ;
51 properties[ " quartz.threadPool.threadCount " ] = " 5 " ;
52 properties[ " quartz.threadPool.threadPriority " ] = " Normal " ;
53 properties[ " quartz.jobStore.misfireThreshold " ] = " 60000 " ;
54 properties[ " quartz.jobStore.type " ] = " Quartz.Impl.AdoJobStore.JobStoreTX, Quartz " ;
55 properties[ " quartz.jobStore.driverDelegateType " ] = " Quartz.Impl.AdoJobStore.StdAdoDelegate, Quartz " ;
56 properties[ " quartz.jobStore.useProperties " ] = " false " ;
57 properties[ " quartz.jobStore.dataSource " ] = " default " ;
58 properties[ " quartz.jobStore.tablePrefix " ] = " QRTZ_ " ;
59 properties[ " quartz.jobStore.clustered " ] = " true " ;
60 // if running MS SQL Server we need this
61 properties[ " quartz.jobStore.selectWithLockSQL " ] = " SELECT * FROM {0}LOCKS UPDLOCK WHERE LOCK_NAME = @lockName " ;
62
63 properties[ " quartz.dataSource.default.connectionString " ] = @" Server=LIJUNNIN-PC\SQLEXPRESS;Database=quartz;Trusted_Connection=True; " ;
64 properties[ " quartz.dataSource.default.provider " ] = " SqlServer-20 " ;
65
66 // First we must get a reference to a scheduler
67 ISchedulerFactory sf = new StdSchedulerFactory(properties);
68 IScheduler sched = sf.GetScheduler();
69 if (inClearJobs)
70 {
71 CleanUp(sched);
72 }
73
74
75 _log.Info( " ------- Initialization Complete ----------- " );
76
77 if (inScheduleJobs)
78 {
79 _log.Info( " ------- Scheduling Jobs ------------------ " );
80
81 string schedId = sched.SchedulerInstanceId;
82
83 int count = 1 ;
84
85 JobDetail job = new JobDetail( " job_ " + count, schedId, typeof (SimpleQuartzJob));
86 // ask scheduler to re-Execute this job if it was in progress when
87 // the scheduler went down...
88 job.RequestsRecovery = true ;
89 SimpleTrigger trigger = new SimpleTrigger( " trig_ " + count, schedId, 20 , 5000L );
90
91 trigger.StartTime = DateTime.Now.AddMilliseconds( 1000L );
92 sched.ScheduleJob(job, trigger);
93 _log.Info( string .Format( " {0} will run at: {1} and repeat: {2} times, every {3} seconds " , job.FullName, trigger.GetNextFireTime(), trigger.RepeatCount, (trigger.RepeatInterval / 1000 )));
94
95 count ++ ;
96 job = new JobDetail( " job_ " + count, schedId, typeof (SimpleQuartzJob));
97 // ask scheduler to re-Execute this job if it was in progress when
98 // the scheduler went down...
99 job.RequestsRecovery = ( true );
100 trigger = new SimpleTrigger( " trig_ " + count, schedId, 20 , 5000L );
101
102 trigger.StartTime = (DateTime.Now.AddMilliseconds( 2000L ));
103 sched.ScheduleJob(job, trigger);
104 _log.Info( string .Format( " {0} will run at: {1} and repeat: {2} times, every {3} seconds " , job.FullName, trigger.GetNextFireTime(), trigger.RepeatCount, (trigger.RepeatInterval / 1000 )));
105
106 count ++ ;
107 job = new JobDetail( " job_ " + count, schedId, typeof (SimpleQuartzJob));
108 // ask scheduler to re-Execute this job if it was in progress when
109 // the scheduler went down...
110 job.RequestsRecovery = ( true );
111 trigger = new SimpleTrigger( " trig_ " + count, schedId, 20 , 3000L );
112
113 trigger.StartTime = (DateTime.Now.AddMilliseconds( 1000L ));
114 sched.ScheduleJob(job, trigger);
115 _log.Info( string .Format( " {0} will run at: {1} and repeat: {2} times, every {3} seconds " , job.FullName, trigger.GetNextFireTime(), trigger.RepeatCount, (trigger.RepeatInterval / 1000 )));
116
117 count ++ ;
118 job = new JobDetail( " job_ " + count, schedId, typeof (SimpleQuartzJob));
119 // ask scheduler to re-Execute this job if it was in progress when
120 // the scheduler went down...
121 job.RequestsRecovery = ( true );
122 trigger = new SimpleTrigger( " trig_ " + count, schedId, 20 , 4000L );
123
124 trigger.StartTime = (DateTime.Now.AddMilliseconds( 1000L ));
125 sched.ScheduleJob(job, trigger);
126 _log.Info( string .Format( " {0} will run at: {1} & repeat: {2}/{3} " , job.FullName, trigger.GetNextFireTime(), trigger.RepeatCount, trigger.RepeatInterval));
127
128 count ++ ;
129 job = new JobDetail( " job_ " + count, schedId, typeof (SimpleQuartzJob));
130 // ask scheduler to re-Execute this job if it was in progress when
131 // the scheduler went down...
132 job.RequestsRecovery = ( true );
133 trigger = new SimpleTrigger( " trig_ " + count, schedId, 20 , 4500L );
134
135 trigger.StartTime = (DateTime.Now.AddMilliseconds( 1000L ));
136 sched.ScheduleJob(job, trigger);
137 _log.Info( string .Format( " {0} will run at: {1} & repeat: {2}/{3} " , job.FullName, trigger.GetNextFireTime(), trigger.RepeatCount, trigger.RepeatInterval));
138 }
139 // jobs don't start firing until start() has been called...
140 _log.Info( " ------- Starting Scheduler --------------- " );
141 sched.Start();
142 _log.Info( " ------- Started Scheduler ---------------- " );
143
144 _log.Info( " ------- Waiting for one hour... ---------- " );
145
146 Thread.Sleep(TimeSpan.FromHours( 1 ));
147
148
149 _log.Info( " ------- Shutting Down -------------------- " );
150 sched.Shutdown();
151 _log.Info( " ------- Shutdown Complete ---------------- " );
152 }
153
154 public void Run()
155 {
156 bool clearJobs = true ;
157 bool scheduleJobs = true ;
158
159 AdoJobStoreRunner example = new AdoJobStoreRunner();
160 example.Run(clearJobs, scheduleJobs);
161
162 }
163 }
164 }
165

 

 Quartz.net 作业调度框架所提供的 API 在两方面都表现极佳:既全面强大,又易于使用。Quartz 可以用于简单的作业触发,也可以用于复杂的 Ado.net持久的作业存储和执行。

 

你可能感兴趣的:(quartz)