Quartz.NET
Quartz.NET 是一个定时计划任务的框架,支持 .NET Core。
本文示例代码大部分来自于官方教程:Quartz.NET - Quartz.NET 3.x Tutorial
Target Framework:.NET Core 2.2
Quartz:3.0.7
使用前首先要使用 NuGet 安装 Quartz.NET。
Install-Package Quartz
标准的定时任务写法
// construct a scheduler factory
NameValueCollection props = new NameValueCollection
{
{ "quartz.serializer.type", "binary" }
};
StdSchedulerFactory factory = new StdSchedulerFactory(props);
// get a scheduler
IScheduler sched = await factory.GetScheduler();
await sched.Start();
// define the job and tie it to our HelloJob class
IJobDetail job = JobBuilder.Create<HelloJob>()
.WithIdentity("myJob", "group1")
.Build();
// Trigger the job to run now, and then every 40 seconds
ITrigger trigger = TriggerBuilder.Create()
.WithIdentity("myTrigger", "group1")
.StartNow()
.WithSimpleSchedule(x => x
.WithIntervalInSeconds(40)
.RepeatForever())
.Build();
await sched.ScheduleJob(job, trigger);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
SchedulerFactory
StdSchedulerFactory
StdSchedulerFactory
继承 ISchedulerFactory
接口。
调用无参构造函数时自动加载根目录的 quartz.config 配置文件;也可以调用带有 NameValueCollection 参数的构造函数通过代码的方式配置。
实例化 StdSchedulerFactory
后调用 getScheduler()
方法生产 Scheduler,初始化 线程池、JobStore、数据源等。
DirectSchedulerFactory
需要使用更多自定义的 Scheduler 时可以使用 DirectSchedulerFactory
,该类同样继承 ISchedulerFactory
接口。
官方文档不建议在不了解时使用该 Factory。
IJob
Job
具体的 Job 继承 IJob
接口,这个接口只有一个 Execute
方法。
namespace Quartz
{
public interface IJob
{
Task Execute(JobExecutionContext context);
}
}
2
3
4
5
6
7
HelloJob.cs
class HelloJob : IJob
{
public async Task Execute(IJobExecutionContext context)
{
await Console.Out.WriteLineAsync("Greetings from HelloJob!");
}
}
2
3
4
5
6
7
可通过 WithIdentity()
扩展方法指定 Job 的名称(name)和组(group),这两个值组成了这个 Job 的 Key({group}.{name}),这个 key 在整个 Scheduler 必须是唯一的。
这些信息储存在 context.JobDetail
中。
JobDataMap
可通过 UsingJobData
扩展方法 JobDataMap
的值。
// define the job and tie it to our DumbJob class
IJobDetail job = JobBuilder.Create<DumbJob>()
.WithIdentity("myJob", "group1") // name "myJob", group "group1"
.UsingJobData("jobSays", "Hello World!")
.UsingJobData("myFloatValue", 3.141f)
.Build();
2
3
4
5
6
可通过 context.JobDetail.JobDataMap
获取。
JobDataMap dataMap = context.JobDetail.JobDataMap;
string jobSays = dataMap.GetString("jobSays");
float myFloatValue = dataMap.GetFloat("myFloatValue");
2
3
4
JobDataMap
中值的获取支持注入:
public class DumbJob : IJob
{
public string JobSays { private get; set; }
public float MyFloatValue { private get; set; }
public async Task Execute(IJobExecutionContext context)
{
JobKey key = context.JobDetail.Key;
JobDataMap dataMap = context.MergedJobDataMap; // Note the difference from the previous example
IList<DateTimeOffset> state = (IList<DateTimeOffset>)dataMap["myStateData"];
state.Add(DateTimeOffset.UtcNow);
await Console.Error.WriteLineAsync("Instance " + key + " of DumbJob says: " + JobSays + ", and val is: " + MyFloatValue);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Job 的状态和并发
DisallowConcurrentExecution
特性在 Job class 上添加该特性可以禁止同一个 JobDetail 中同一时间运行多个 Job 实例(但可以定义多个 JobDetail 使用同一个 Job class);
PersistJobDataAfterExecution
特性在 Job class 上添加该特性可以保留上一次运行结束后的 JobDataMap 到下一次运行。考虑到并发运行的可能最好和
DisallowConcurrentExecution
一起使用。
Job class 属性使用注入时,修改属性的值并不会自动修改 JobDataMap 中的值,需要JobDataMap.Put
方法更新其中的值。csharpcontext.JobDetail.JobDataMap.Put("count", Count);
1
Job 的其它属性
Durability
:是否持久化;为 false 时如果没有任意一个激活的 Trigger,则将其从 Scheduler 中删除;默认值为 false;RequestsRecovery
:进程意外结束(如 PC 意外关机)时,被中断的任务是否重新执行;为 true 时会再次运行;默认值为 false;
ITrigger
ITrigger 的 Schedule 扩展方法:
WithCalendarIntervalSchedule
:以指定的时间间隔(年、月、周、日、时、分、秒、毫秒)重复执行;WithCronSchedule
:使用 Cron 表达式指定何时执行;WithSimpleSchedule
:以指定时间间隔重复执行指定的次数或永久执行;WithDailyTimeIntervalSchedule
:可指定每天在何时开始运行、何时停止运行、每天运行多少次;
属性
- JobKey :
{group}.{name}
- StartTimeUtc
- EndTimeUtc
- Priority:优先级(默认值为 5)
Misfire Instructions 计划失败时的指令
在 Schedule 中通过 WithMisfireHandlingInstructionFireNow()
等方法指定。默认使用 smart policy。
By default they use a ‘smart policy’ instruction - which has dynamic behavior based on trigger type and configuration.
ITrigger myTrigger = TriggerBuilder.Create()
.WithIdentity("myTrigger", "group1")
.StartNow()
.WithSimpleSchedule(x => x
.WithIntervalInSeconds(10)
.RepeatForever()
.WithMisfireHandlingInstructionFireNow()
)
.Build();
2
3
4
5
6
7
8
9
可通过 myTrigger.MisfireInstruction
属性获取该设置,默认值为 MisfireInstruction.InstructionNotSet
。
namespace Quartz
{
//
// 摘要:
// Misfire instructions.
public struct MisfireInstruction
{
//
// 摘要:
// Instruction not set (yet).
public const int InstructionNotSet = 0;
//
// 摘要:
// Use smart policy.
public const int SmartPolicy = 0;
//
// 摘要:
// Instructs the Quartz.IScheduler that the Quartz.ITrigger will never be evaluated
// for a misfire situation, and that the scheduler will simply try to fire it as
// soon as it can, and then update the Trigger as if it had fired at the proper
// time.
//
// 备注:
// NOTE: if a trigger uses this instruction, and it has missed several of its scheduled
// firings, then several rapid firings may occur as the trigger attempt to catch
// back up to where it would have been. For example, a SimpleTrigger that fires
// every 15 seconds which has misfired for 5 minutes will fire 20 times once it
// gets the chance to fire.
public const int IgnoreMisfirePolicy = -1;
//
// 摘要:
// Misfire policy settings for SimpleTrigger.
public struct SimpleTrigger
{
//
// 摘要:
// Instructs the Quartz.IScheduler that upon a mis-fire situation, the Quartz.ISimpleTrigger
// wants to be fired now by Quartz.IScheduler.
// NOTE: This instruction should typically only be used for 'one-shot' (non-repeating)
// Triggers. If it is used on a trigger with a repeat count > 0 then it is equivalent
// to the instruction Quartz.MisfireInstruction.SimpleTrigger.RescheduleNowWithRemainingRepeatCount.
public const int FireNow = 1;
//
// 摘要:
// Instructs the Quartz.IScheduler that upon a mis-fire situation, the Quartz.ISimpleTrigger
// wants to be re-scheduled to 'now' (even if the associated Quartz.ICalendar excludes
// 'now') with the repeat count left as-is. This does obey the Quartz.ITrigger end-time
// however, so if 'now' is after the end-time the Quartz.ITrigger will not fire
// again.
//
// 备注:
// NOTE: Use of this instruction causes the trigger to 'forget' the start-time and
// repeat-count that it was originally setup with (this is only an issue if you
// for some reason wanted to be able to tell what the original values were at some
// later time).
public const int RescheduleNowWithExistingRepeatCount = 2;
//
// 摘要:
// Instructs the Quartz.IScheduler that upon a mis-fire situation, the Quartz.ISimpleTrigger
// wants to be re-scheduled to 'now' (even if the associated Quartz.ICalendar excludes
// 'now') with the repeat count set to what it would be, if it had not missed any
// firings. This does obey the Quartz.ITrigger end-time however, so if 'now' is
// after the end-time the Quartz.ITrigger will not fire again.
// NOTE: Use of this instruction causes the trigger to 'forget' the start-time and
// repeat-count that it was originally setup with. Instead, the repeat count on
// the trigger will be changed to whatever the remaining repeat count is (this is
// only an issue if you for some reason wanted to be able to tell what the original
// values were at some later time).
// NOTE: This instruction could cause the Quartz.ITrigger to go to the 'COMPLETE'
// state after firing 'now', if all the repeat-fire-times where missed.
public const int RescheduleNowWithRemainingRepeatCount = 3;
//
// 摘要:
// Instructs the Quartz.IScheduler that upon a mis-fire situation, the Quartz.ISimpleTrigger
// wants to be re-scheduled to the next scheduled time after 'now' - taking into
// account any associated Quartz.ICalendar, and with the repeat count set to what
// it would be, if it had not missed any firings.
//
// 备注:
// NOTE/WARNING: This instruction could cause the Quartz.ITrigger to go directly
// to the 'COMPLETE' state if all fire-times where missed.
public const int RescheduleNextWithRemainingCount = 4;
//
// 摘要:
// Instructs the Quartz.IScheduler that upon a mis-fire situation, the Quartz.ISimpleTrigger
// wants to be re-scheduled to the next scheduled time after 'now' - taking into
// account any associated Quartz.ICalendar, and with the repeat count left unchanged.
//
// 备注:
// NOTE/WARNING: This instruction could cause the Quartz.ITrigger to go directly
// to the 'COMPLETE' state if all the end-time of the trigger has arrived.
public const int RescheduleNextWithExistingCount = 5;
}
//
// 摘要:
// misfire instructions for CronTrigger
public struct CronTrigger
{
//
// 摘要:
// Instructs the Quartz.IScheduler that upon a mis-fire situation, the Quartz.ICronTrigger
// wants to be fired now by Quartz.IScheduler.
public const int FireOnceNow = 1;
//
// 摘要:
// Instructs the Quartz.IScheduler that upon a mis-fire situation, the Quartz.ICronTrigger
// wants to have it's next-fire-time updated to the next time in the schedule after
// the current time (taking into account any associated Quartz.ICalendar), but it
// does not want to be fired now.
public const int DoNothing = 2;
}
//
// 摘要:
// Misfire instructions for DateIntervalTrigger
public struct CalendarIntervalTrigger
{
//
// 摘要:
// Instructs the Quartz.IScheduler that upon a mis-fire situation, the Quartz.ICalendarIntervalTrigger
// wants to be fired now by Quartz.IScheduler.
public const int FireOnceNow = 1;
//
// 摘要:
// Instructs the Quartz.IScheduler that upon a mis-fire situation, the Quartz.ICalendarIntervalTrigger
// wants to have it's next-fire-time updated to the next time in the schedule after
// the current time (taking into account any associated Quartz.ICalendar), but it
// does not want to be fired now.
public const int DoNothing = 2;
}
//
// 摘要:
// Misfire instructions for DailyTimeIntervalTrigger
public struct DailyTimeIntervalTrigger
{
//
// 摘要:
// Instructs the Quartz.IScheduler that upon a mis-fire situation, the Quartz.IDailyTimeIntervalTrigger
// wants to be fired now by Quartz.IScheduler.
public const int FireOnceNow = 1;
//
// 摘要:
// Instructs the Quartz.IScheduler that upon a mis-fire situation, the Quartz.MisfireInstruction.DailyTimeIntervalTrigger
// wants to have it's next-fire-time updated to the next time in the schedule after
// the current time (taking into account any associated Quartz.ICalendar), but it
// does not want to be fired now.
public const int DoNothing = 2;
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
Calendars 日历
可以通过指定日历来排除执行的日期(如假日不执行)。
同一个日历可以用于多个 Trigger。
HolidayCalendar cal = new HolidayCalendar();
cal.AddExcludedDate(someDate);
await sched.AddCalendar("myHolidays", cal, false);
ITrigger t = TriggerBuilder.Create()
.WithIdentity("myTrigger")
.ForJob("myJob")
.WithSchedule(CronScheduleBuilder.DailyAtHourAndMinute(9, 30)) // execute job daily at 9:30
.ModifiedByCalendar("myHolidays") // but not on holidays
.Build();
// .. schedule job with trigger
ITrigger t2 = TriggerBuilder.Create()
.WithIdentity("myTrigger2")
.ForJob("myJob2")
.WithSchedule(CronScheduleBuilder.DailyAtHourAndMinute(11, 30)) // execute job daily at 11:30
.ModifiedByCalendar("myHolidays") // but not on holidays
.Build();
// .. schedule job with trigger2
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
自带的 Calendar 实现
位于 Quartz.Impl.Calendar 命名空间下。
- AnnualCalendar:排除每年的某一天;
- BaseCalendar:基础实现;
- CronCalendar:基于 Cron 表达式排除日期;
- DailyCalendar:排除每天的某个时间段;
- HolidayCalendar:排除固定的某天;
- MonthlyCalendar:排除每月的某天;
- WeeklyCalendar:排除每周的某天;
自定义 Calendar
自定义 Calendar 时需实现 ICalendar
接口。
namespace Quartz
{
public interface ICalendar
{
string Description { get; set; }
ICalendar CalendarBase { set; get; }
bool IsTimeIncluded(DateTimeOffset timeUtc);
DateTime GetNextIncludedTimeUtc(DateTimeOffset timeUtc);
ICalendar Clone();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
SimpleTrigger
几种常用的示例
仅在特定的时间执行一次
csharp// trigger builder creates simple trigger by default, actually an ITrigger is returned ISimpleTrigger trigger = (ISimpleTrigger) TriggerBuilder.Create() .WithIdentity("trigger1", "group1") .StartAt(myStartTime) // some Date .ForJob("job1", "group1") // identify job with name, group strings .Build();
1
2
3
4
5
6从指定的时间开始,每隔 10 秒执行一次,共执行 10 次
csharptrigger = TriggerBuilder.Create() .WithIdentity("trigger3", "group1") .StartAt(myTimeToStartFiring) // if a start time is not given (if this line were omitted), "now" is implied .WithSimpleSchedule(x => x .WithIntervalInSeconds(10) .WithRepeatCount(10)) // note that 10 repeats will give a total of 11 firings .ForJob(myJob) // identify job with handle to its JobDetail itself .Build();
1
2
3
4
5
6
7
8五分钟后执行一次
csharptrigger = (ISimpleTrigger) TriggerBuilder.Create() .WithIdentity("trigger5", "group1") .StartAt(DateBuilder.FutureDate(5, IntervalUnit.Minute)) // use DateBuilder to create a date in the future .ForJob(myJobKey) // identify job with its JobKey .Build();
1
2
3
4
5从现在开始每 5 分钟执行一次,直到 22:00
csharptrigger = TriggerBuilder.Create() .WithIdentity("trigger7", "group1") .WithSimpleSchedule(x => x .WithIntervalInMinutes(5) .RepeatForever()) .EndAt(DateBuilder.DateOf(22, 0, 0)) .Build();
1
2
3
4
5
6
7下一个偶数的整点开始执行,之后每隔 2 小时执行一次
csharptrigger = TriggerBuilder.Create() .WithIdentity("trigger8") // because group is not specified, "trigger8" will be in the default group .StartAt(DateBuilder.EvenHourDate(null)) // get the next even-hour (minutes and seconds zero ("00:00")) .WithSimpleSchedule(x => x .WithIntervalInHours(2) .RepeatForever()) // note that in this example, 'forJob(..)' is not called // - which is valid if the trigger is passed to the scheduler along with the job .Build();
1
2
3
4
5
6
7
8
9
SimpleTrigger Misfire Instructions
对于 SimpleTrigger 有以下几种 Misfire Instructions,每个都要一个对应的 SimpleScheduleBuilder
方法。
MisfireInstruction.IgnoreMisfirePolicy
MisfirePolicy.SimpleTrigger.FireNow
MisfirePolicy.SimpleTrigger.RescheduleNowWithExistingRepeatCount
MisfirePolicy.SimpleTrigger.RescheduleNowWithRemainingRepeatCount
MisfirePolicy.SimpleTrigger.RescheduleNextWithRemainingCount
MisfirePolicy.SimpleTrigger.RescheduleNextWithExistingCount
trigger = TriggerBuilder.Create()
.WithIdentity("trigger7", "group1")
.WithSimpleSchedule(x => x
.WithIntervalInMinutes(5)
.RepeatForever()
.WithMisfireHandlingInstructionNextWithExistingCount())
.Build();
2
3
4
5
6
7
public SimpleScheduleBuilder WithMisfireHandlingInstructionFireNow();
public SimpleScheduleBuilder WithMisfireHandlingInstructionIgnoreMisfires();
public SimpleScheduleBuilder WithMisfireHandlingInstructionNextWithExistingCount();
public SimpleScheduleBuilder WithMisfireHandlingInstructionNextWithRemainingCount();
public SimpleScheduleBuilder WithMisfireHandlingInstructionNowWithExistingCount();
public SimpleScheduleBuilder WithMisfireHandlingInstructionNowWithRemainingCount();
2
3
4
5
6
CronTrigger
可以通过 ITrigger 的 WithCronSchedule
扩展方法创建 CronTrigger,或者使用 WithSchedule
扩展方法 + CronScheduleBuilder
实现同样的效果。
下面的两个示例均表示在每周三的 10 点 42 分(美国时区)执行。
trigger = TriggerBuilder.Create()
.WithIdentity("trigger3", "group1")
.WithCronSchedule("0 42 10 ? * WED", x => x
.InTimeZone(TimeZoneInfo.FindSystemTimeZoneById("Central America Standard Time")))
.ForJob(myJobKey)
.Build();
2
3
4
5
6
trigger = TriggerBuilder.Create()
.WithIdentity("trigger3", "group1")
.WithSchedule(CronScheduleBuilder
.WeeklyOnDayAndHourAndMinute(DayOfWeek.Wednesday, 10, 42)
.InTimeZone(TimeZoneInfo.FindSystemTimeZoneById("Central America Standard Time")))
.ForJob(myJobKey)
.Build();
2
3
4
5
6
7
Cron Expressions(Cron 表达式)
Cron 表达式有 7 个组成部分,每个部分之间以空格分隔。
详细说明 => Jenkins – Poll SCM – 日程表 中的表达式写法 中的 Cron 表达式 部分。
CronTrigger Misfire Instructions
MisfireInstruction.IgnoreMisfirePolicy
MisfireInstruction.CronTrigger.DoNothing
MisfireInstruction.CronTrigger.FireOnceNow
trigger = TriggerBuilder.Create()
.WithIdentity("trigger3", "group1")
.WithCronSchedule("0 0/2 8-17 * * ?", x => x
.WithMisfireHandlingInstructionFireAndProceed())
.ForJob("myJob", "group1")
.Build();
2
3
4
5
6
public CronScheduleBuilder WithMisfireHandlingInstructionDoNothing();
public CronScheduleBuilder WithMisfireHandlingInstructionFireAndProceed();
public CronScheduleBuilder WithMisfireHandlingInstructionIgnoreMisfires();
2
3
TriggerListeners and JobListeners
监听 Trigger 和 Job 中的事件。
TriggerListeners
TriggerListener 可以监听 Trigger 执行(TriggerFired、VetoJobExecution)、Trigger 未执行(TriggerMisfired)、Trigger 完成(TriggerComplete);
TriggerFired 方法最先执行,结束后调用 VetoJobExecution 方法。
如果 VetoJobExecution 方法返回 true,则 Job 不会被执行,并且不会触发 Trigger 完成事件。
ITriggerListener
//
// 摘要:
// The interface to be implemented by classes that want to be informed when a Quartz.ITrigger
// fires. In general, applications that use a Quartz.IScheduler will not have use
// for this mechanism.
public interface ITriggerListener
{
//
// 摘要:
// Get the name of the Quartz.ITriggerListener.
string Name { get; }
//
// 摘要:
// Called by the Quartz.IScheduler when a Quartz.ITrigger has fired, it's associated
// Quartz.IJobDetail has been executed, and it's Quartz.Spi.IOperableTrigger.Triggered(Quartz.ICalendar)
// method has been called.
//
// 参数:
// trigger:
// The Quartz.ITrigger that was fired.
//
// context:
// The Quartz.IJobExecutionContext that was passed to the Quartz.IJob'sQuartz.IJob.Execute(Quartz.IJobExecutionContext)
// method.
//
// triggerInstructionCode:
// The result of the call on the Quartz.ITrigger'sQuartz.Spi.IOperableTrigger.Triggered(Quartz.ICalendar)
// method.
//
// cancellationToken:
// The cancellation instruction.
Task TriggerComplete(ITrigger trigger, IJobExecutionContext context, SchedulerInstruction triggerInstructionCode, CancellationToken cancellationToken = default(CancellationToken));
//
// 摘要:
// Called by the Quartz.IScheduler when a Quartz.ITrigger has fired, and it's associated
// Quartz.IJobDetail is about to be executed.
// It is called before the Quartz.ITriggerListener.VetoJobExecution(Quartz.ITrigger,Quartz.IJobExecutionContext,System.Threading.CancellationToken)
// method of this interface.
//
// 参数:
// trigger:
// The Quartz.ITrigger that has fired.
//
// context:
// The Quartz.IJobExecutionContext that will be passed to the Quartz.IJob'sQuartz.IJob.Execute(Quartz.IJobExecutionContext)
// method.
//
// cancellationToken:
// The cancellation instruction.
Task TriggerFired(ITrigger trigger, IJobExecutionContext context, CancellationToken cancellationToken = default(CancellationToken));
//
// 摘要:
// Called by the Quartz.IScheduler when a Quartz.ITrigger has misfired.
// Consideration should be given to how much time is spent in this method, as it
// will affect all triggers that are misfiring. If you have lots of triggers misfiring
// at once, it could be an issue it this method does a lot.
//
// 参数:
// trigger:
// The Quartz.ITrigger that has misfired.
//
// cancellationToken:
// The cancellation instruction.
Task TriggerMisfired(ITrigger trigger, CancellationToken cancellationToken = default(CancellationToken));
//
// 摘要:
// Called by the Quartz.IScheduler when a Quartz.ITrigger has fired, and it's associated
// Quartz.IJobDetail is about to be executed.
// It is called after the Quartz.ITriggerListener.TriggerFired(Quartz.ITrigger,Quartz.IJobExecutionContext,System.Threading.CancellationToken)
// method of this interface. If the implementation vetoes the execution (via returning
// true), the job's execute method will not be called.
//
// 参数:
// trigger:
// The Quartz.ITrigger that has fired.
//
// context:
// The Quartz.IJobExecutionContext that will be passed to the Quartz.IJob'sQuartz.IJob.Execute(Quartz.IJobExecutionContext)
// method.
//
// cancellationToken:
// The cancellation instruction.
//
// 返回结果:
// Returns true if job execution should be vetoed, false otherwise.
Task<bool> VetoJobExecution(ITrigger trigger, IJobExecutionContext context, CancellationToken cancellationToken = default(CancellationToken));
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
MyTriggerListener.cs
class MyTriggerListener : ITriggerListener
{
public string Name { get; set; }
public async Task TriggerComplete(ITrigger trigger, IJobExecutionContext context, SchedulerInstruction triggerInstructionCode, CancellationToken cancellationToken = default(CancellationToken))
{
await Console.Out.WriteLineAsync($"MyTriggerListener({Name}.{trigger.Key}).TriggerComplete was executed at {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}.");
}
public async Task TriggerFired(ITrigger trigger, IJobExecutionContext context, CancellationToken cancellationToken = default(CancellationToken))
{
await Console.Out.WriteLineAsync($"MyTriggerListener({Name}.{trigger.Key}).TriggerFired was executed at {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}.");
}
public async Task TriggerMisfired(ITrigger trigger, CancellationToken cancellationToken = default(CancellationToken))
{
await Console.Out.WriteLineAsync($"MyTriggerListener({Name}.{trigger.Key}).TriggerMisfired was executed at {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}.");
}
public async Task<bool> VetoJobExecution(ITrigger trigger, IJobExecutionContext context, CancellationToken cancellationToken = default(CancellationToken))
{
await Console.Out.WriteLineAsync($"MyTriggerListener({Name}.{trigger.Key}).VetoJobExecution was executed at {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}.");
return false;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
JobListeners
JobListener 可以监听 Job 即将被执行(JobToBeExecuted)、Job 执行结束(JobWasExecuted);
JobExecutionVetoed 方法则会在 TriggerListener.VetoJobExecution 返回值为 true 时执行。
IJobListener
//
// 摘要:
// The interface to be implemented by classes that want to be informed when a Quartz.IJobDetail
// executes. In general, applications that use a Quartz.IScheduler will not have
// use for this mechanism.
public interface IJobListener
{
//
// 摘要:
// Get the name of the Quartz.IJobListener.
string Name { get; }
//
// 摘要:
// Called by the Quartz.IScheduler when a Quartz.IJobDetail was about to be executed
// (an associated Quartz.ITrigger has occurred), but a Quartz.ITriggerListener vetoed
// it's execution.
Task JobExecutionVetoed(IJobExecutionContext context, CancellationToken cancellationToken = default(CancellationToken));
//
// 摘要:
// Called by the Quartz.IScheduler when a Quartz.IJobDetail is about to be executed
// (an associated Quartz.ITrigger has occurred).
// This method will not be invoked if the execution of the Job was vetoed by a Quartz.ITriggerListener.
Task JobToBeExecuted(IJobExecutionContext context, CancellationToken cancellationToken = default(CancellationToken));
//
// 摘要:
// Called by the Quartz.IScheduler after a Quartz.IJobDetail has been executed,
// and be for the associated Quartz.Spi.IOperableTrigger's Quartz.Spi.IOperableTrigger.Triggered(Quartz.ICalendar)
// method has been called.
Task JobWasExecuted(IJobExecutionContext context, JobExecutionException jobException, CancellationToken cancellationToken = default(CancellationToken));
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
MyJobListener.cs
class MyJobListener : IJobListener
{
public string Name { get; set; }
public async Task JobExecutionVetoed(IJobExecutionContext context, CancellationToken cancellationToken = default(CancellationToken))
{
await Console.Out.WriteLineAsync($"MyJobListener({Name}.{context.JobDetail.Key}).JobExecutionVetoed was executed at {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}.");
}
public async Task JobToBeExecuted(IJobExecutionContext context, CancellationToken cancellationToken = default(CancellationToken))
{
await Console.Out.WriteLineAsync($"MyJobListener({Name}.{context.JobDetail.Key}).JobToBeExecuted was executed at {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}.");
}
public async Task JobWasExecuted(IJobExecutionContext context, JobExecutionException jobException, CancellationToken cancellationToken = default(CancellationToken))
{
await Console.Out.WriteLineAsync($"MyJobListener({Name}.{context.JobDetail.Key}).JobWasExecuted was executed at {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}.");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
使用 Listener
监听某个特定的 Job
scheduler.ListenerManager.AddJobListener(myJobListener, KeyMatcher<JobKey>.KeyEquals(new JobKey("myJobName", "myJobGroup")));
监听某个特定组的 Job
scheduler.ListenerManager.AddJobListener(myJobListener, GroupMatcher<JobKey>.GroupEquals("myJobGroup"));
监听两个特定组的 Job
scheduler.ListenerManager.AddJobListener(myJobListener,
OrMatcher<JobKey>.Or(GroupMatcher<JobKey>.GroupEquals("myJobGroup"), GroupMatcher<JobKey>.GroupEquals("yourGroup")));
2
监听所有的 Job
scheduler.ListenerManager.AddJobListener(myJobListener, GroupMatcher<JobKey>.AnyGroup());
TriggerListener 用法类似。
监听所有的 Trigger
scheduler.ListenerManager.AddTriggerListener(myTriggerListener, GroupMatcher<TriggerKey>.AnyGroup());
运行结果
从下面打印的结果可以看出 TriggerListener 和 JobListener 中方法的调用顺序。
TriggerListener.TriggerFired => TriggerListener.VetoJobExecution => JobListener.JobToBeExecuted => Job 执行 => JobListener.JobWasExecuted => TriggerListener.TriggerComplete
MyTriggerListener(trigger-listener-1.group1.trigger1).TriggerFired was executed at 2019-02-12 13:55:29.758.
MyTriggerListener(trigger-listener-1.group1.trigger1).VetoJobExecution was executed at 2019-02-12 13:55:29.759.
MyJobListener(job-listener-1.group1.job1).JobToBeExecuted was executed at 2019-02-12 13:55:29.764.
Greetings from HelloJob(group1.job1 - 636855477297106001)!
MyJobListener(job-listener-1.group1.job1).JobWasExecuted was executed at 2019-02-12 13:55:29.770.
MyTriggerListener(trigger-listener-1.group1.trigger1).TriggerComplete was executed at 2019-02-12 13:55:29.776.
2
3
4
5
6
如果将 MyTriggerListener.VetoJobExecution 方法的返回值改为 true,则会执行 MyJobListener.JobExecutionVetoed 方法,并且 Job 不会被执行。
TriggerListener.TriggerFired => TriggerListener.VetoJobExecution => JobListener.JobExecutionVetoed
输出结果如下:
MyTriggerListener(trigger-listener-1.group1.trigger1).TriggerFired was executed at 2019-02-12 14:03:48.986.
MyTriggerListener(trigger-listener-1.group1.trigger1).VetoJobExecution was executed at 2019-02-12 14:03:48.987.
MyJobListener(job-listener-1.group1.job1).JobExecutionVetoed was executed at 2019-02-12 14:03:48.991.
2
3
SchedulerListeners
效果和用法同上面的两个 Listener 类似,只是监听的事件不一样。
SchedulerListener 监听的是 Scheduler 关联的事件,如 添加/删除 Trigger 或 Job、Scheduler 的异常、Scheduler 的关闭等。
ISchedulerListener
//
// 摘要:
// The interface to be implemented by classes that want to be informed of major
// Quartz.IScheduler events.
public interface ISchedulerListener
{
//
// 摘要:
// Called by the Quartz.IScheduler when a Quartz.IJobDetail has been added.
Task JobAdded(IJobDetail jobDetail, CancellationToken cancellationToken = default(CancellationToken));
//
// 摘要:
// Called by the Quartz.IScheduler when a Quartz.IJobDetail has been deleted.
Task JobDeleted(JobKey jobKey, CancellationToken cancellationToken = default(CancellationToken));
//
// 摘要:
// Called by the Quartz.IScheduler when a Quartz.IJobDetail has been interrupted.
Task JobInterrupted(JobKey jobKey, CancellationToken cancellationToken = default(CancellationToken));
//
// 摘要:
// Called by the Quartz.IScheduler when a Quartz.IJobDetail has been paused.
Task JobPaused(JobKey jobKey, CancellationToken cancellationToken = default(CancellationToken));
//
// 摘要:
// Called by the Quartz.IScheduler when a Quartz.IJobDetail has been un-paused.
Task JobResumed(JobKey jobKey, CancellationToken cancellationToken = default(CancellationToken));
//
// 摘要:
// Called by the Quartz.IScheduler when a Quartz.IJobDetail is scheduled.
Task JobScheduled(ITrigger trigger, CancellationToken cancellationToken = default(CancellationToken));
//
// 摘要:
// Called by the Quartz.IScheduler when a group of Quartz.IJobDetails has been paused.
// If all groups were paused, then the jobName parameter will be null. If all jobs
// were paused, then both parameters will be null.
//
// 参数:
// jobGroup:
// The job group.
//
// cancellationToken:
// The cancellation instruction.
Task JobsPaused(string jobGroup, CancellationToken cancellationToken = default(CancellationToken));
//
// 摘要:
// Called by the Quartz.IScheduler when a Quartz.IJobDetail has been un-paused.
//
// 参数:
// jobGroup:
// The job group.
//
// cancellationToken:
// The cancellation instruction.
Task JobsResumed(string jobGroup, CancellationToken cancellationToken = default(CancellationToken));
//
// 摘要:
// Called by the Quartz.IScheduler when a Quartz.IJobDetail is unscheduled.
Task JobUnscheduled(TriggerKey triggerKey, CancellationToken cancellationToken = default(CancellationToken));
//
// 摘要:
// Called by the Quartz.IScheduler when a serious error has occurred within the
// scheduler - such as repeated failures in the Quartz.Spi.IJobStore, or the inability
// to instantiate a Quartz.IJob instance when its Quartz.ITrigger has fired.
Task SchedulerError(string msg, SchedulerException cause, CancellationToken cancellationToken = default(CancellationToken));
//
// 摘要:
// Called by the Quartz.IScheduler to inform the listener that it has move to standby
// mode.
Task SchedulerInStandbyMode(CancellationToken cancellationToken = default(CancellationToken));
//
// 摘要:
// Called by the Quartz.IScheduler to inform the listener that it has Shutdown.
Task SchedulerShutdown(CancellationToken cancellationToken = default(CancellationToken));
//
// 摘要:
// Called by the Quartz.IScheduler to inform the listener that it has begun the
// shutdown sequence.
Task SchedulerShuttingdown(CancellationToken cancellationToken = default(CancellationToken));
//
// 摘要:
// Called by the Quartz.IScheduler to inform the listener that it has started.
Task SchedulerStarted(CancellationToken cancellationToken = default(CancellationToken));
//
// 摘要:
// Called by the Quartz.IScheduler to inform the listener that it is starting.
Task SchedulerStarting(CancellationToken cancellationToken = default(CancellationToken));
//
// 摘要:
// Called by the Quartz.IScheduler to inform the listener that all jobs, triggers
// and calendars were deleted.
Task SchedulingDataCleared(CancellationToken cancellationToken = default(CancellationToken));
//
// 摘要:
// Called by the Quartz.IScheduler when a Quartz.ITrigger has reached the condition
// in which it will never fire again.
Task TriggerFinalized(ITrigger trigger, CancellationToken cancellationToken = default(CancellationToken));
//
// 摘要:
// Called by the Quartz.IScheduler a Quartz.ITriggers has been paused.
Task TriggerPaused(TriggerKey triggerKey, CancellationToken cancellationToken = default(CancellationToken));
//
// 摘要:
// Called by the Quartz.IScheduler when a Quartz.ITrigger has been un-paused.
Task TriggerResumed(TriggerKey triggerKey, CancellationToken cancellationToken = default(CancellationToken));
//
// 摘要:
// Called by the Quartz.IScheduler a group of Quartz.ITriggers has been paused.
//
// 参数:
// triggerGroup:
// The trigger group.
//
// cancellationToken:
// The cancellation instruction.
//
// 备注:
// If a all groups were paused, then the triggerName parameter will be null.
Task TriggersPaused(string triggerGroup, CancellationToken cancellationToken = default(CancellationToken));
//
// 摘要:
// Called by the Quartz.IScheduler when a group of Quartz.ITriggers has been un-paused.
//
// 参数:
// triggerGroup:
// The trigger group.
//
// cancellationToken:
// The cancellation instruction.
//
// 备注:
// If all groups were resumed, then the triggerName parameter will be null.
Task TriggersResumed(string triggerGroup, CancellationToken cancellationToken = default(CancellationToken));
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
添加 SchedulerListener
scheduler.ListenerManager.AddSchedulerListener(mySchedListener);
删除 SchedulerListener
scheduler.ListenerManager.RemoveSchedulerListener(mySchedListener);
执行输出结果(这里仅有部分方法被调用到):
[14:31:37] [Info] Quartz scheduler 'QuartzScheduler' initialized
[14:31:37] [Info] Quartz scheduler version: 3.0.7.0
[14:31:37] [Info] Scheduler QuartzScheduler_$_NON_CLUSTERED started.
MySchedulerListener(group1.job1).JobAdded was executed at 2019-02-12 14:31:37.395.
MySchedulerListener(group1.trigger1).JobScheduled was executed at 2019-02-12 14:31:37.399.
MyTriggerListener(trigger-listener-1.group1.trigger1).TriggerFired was executed at 2019-02-12 14:31:37.453.
MyTriggerListener(trigger-listener-1.group1.trigger1).VetoJobExecution was executed at 2019-02-12 14:31:37.454.
MyJobListener(job-listener-1.group1.job1).JobToBeExecuted was executed at 2019-02-12 14:31:37.458.
Greetings from HelloJob(group1.job1 - 636855498974031303)!
MyJobListener(job-listener-1.group1.job1).JobWasExecuted was executed at 2019-02-12 14:31:37.464.
MyTriggerListener(trigger-listener-1.group1.trigger1).TriggerComplete was executed at 2019-02-12 14:31:37.471.
MySchedulerListener(group1.trigger1).TriggerFinalized was executed at 2019-02-12 14:31:37.472.
MySchedulerListener(group1.job1).JobDeleted was executed at 2019-02-12 14:31:37.478.
[14:32:37] [Info] Scheduler QuartzScheduler_$_NON_CLUSTERED shutting down.
[14:32:37] [Info] Scheduler QuartzScheduler_$_NON_CLUSTERED paused.
MySchedulerListener.SchedulerInStandbyMode was executed at 2019-02-12 14:32:37.417.
MySchedulerListener.SchedulerShuttingdown was executed at 2019-02-12 14:32:37.419.
MySchedulerListener.SchedulerShutdown was executed at 2019-02-12 14:32:37.424.
[14:32:37] [Info] Scheduler QuartzScheduler_$_NON_CLUSTERED Shutdown complete.
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
JobStores(Job 存储)
RAMJobStore
默认使用的是 RAMJobStore,Job 保存在内存中。进程关闭后所有 Job 信息都会丢失。
quartz.jobStore.type = Quartz.Simpl.RAMJobStore, Quartz
AdoJobStore
AdoJobStore 将 Job、Trigger 设置经由 ADO.NET 保存到数据库。相比 RAMJobStore 没有那么快,但是可以持久化。另外可以通过对 Table 添加索引来提高数据库的效率。
配置项
quartz.jobStore.type
现在 Quartz.Impl.AdoJobStore.JobStoreTX 是唯一的 AdoJobStore 实现。
propertiesquartz.jobStore.type = Quartz.Impl.AdoJobStore.JobStoreTX, Quartz
1quartz.jobStore.driverDelegateType
Quartz.Impl.AdoJobStore.StdAdoDelegate 是个抽象的基类,具体应该设置成对应数据库的代理,如 MySQL 时应设置为 Quartz.Impl.AdoJobStore.MySQLDelegate, Quartz 。
propertiesquartz.jobStore.driverDelegateType = Quartz.Impl.AdoJobStore.StdAdoDelegate, Quartz
1quartz.jobStore.tablePrefix
定义 Quartz 数据库表名的前缀。当在同一个数据库保存多个 Quartz 实例的数据时使用。
QRTZ_ 是官方提供的建表 SQL 文中的表前缀。propertiesquartz.jobStore.tablePrefix = QRTZ_
1quartz.jobStore.dataSource
定义数据源的名称,值可以自定义。
propertiesquartz.jobStore.dataSource = myDS
1quartz.dataSource.myDS
配置上面定义的数据源 myDS 的类型和连接字符串。
propertiesquartz.dataSource.myDS.connectionString = Server=localhost;Database=quartz;Uid=quartznet;Pwd=quartznet quartz.dataSource.myDS.provider = MySql
1
2支持如下数据库 Provider:
- SqlServer - SQL Server driver for .NET Framework 2.0
- OracleODP - Oracle’s Oracle Driver
- OracleODPManaged - Oracle’s managed driver for Oracle 11
- MySql - MySQL Connector/.NET
- SQLite - SQLite ADO.NET Provider
- SQLite-Microsoft - Microsoft SQLite ADO.NET Provider
- Firebird - Firebird ADO.NET Provider
- Npgsql - PostgreSQL Npgsql
示例
这里使用的 SqlServer 数据库。
执行 tables_sqlServer.sql,创建数据库;
其它的数据库结构参照 quartznet/database/tables/ 中的其它文件。
初始化
StdSchedulerFactory
配置 AdoJobStore;使用代码配置时,使用
StdSchedulerFactory(NameValueCollection props)
构造函数,示例如下:csharp// Grab the Scheduler instance from the Factory NameValueCollection props = new NameValueCollection { { "quartz.serializer.type", "binary" }, { "quartz.scheduler.instanceName", "MyScheduler" }, { "quartz.jobStore.type", "Quartz.Impl.AdoJobStore.JobStoreTX, Quartz" }, { "quartz.jobStore.driverDelegateType", "Quartz.Impl.AdoJobStore.SqlServerDelegate, Quartz" }, { "quartz.jobStore.tablePrefix", "QRTZ_" }, { "quartz.jobStore.dataSource", "myDS" }, { "quartz.dataSource.myDS.connectionString", "Data Source=LIUJIAJIA\\LJJ2008;Initial Catalog=Quartz;User ID=sa;Password=ABC123;" }, { "quartz.dataSource.myDS.provider", "SqlServer" }, { "quartz.threadPool.threadCount", "3" }, }; StdSchedulerFactory factory = new StdSchedulerFactory(props); IScheduler scheduler = await factory.GetScheduler(); // and start it off await scheduler.Start(); // define the job and tie it to our HelloJob class IJobDetail job = JobBuilder.Create<HelloJob>() .WithIdentity("job1", "group1") .UsingJobData("count", 2) .Build(); // Trigger the job to run now, and then repeat every 10 seconds ITrigger trigger = TriggerBuilder.Create() .WithIdentity("trigger1", "group1") .StartNow() .WithSimpleSchedule(x => x .WithIntervalInSeconds(10) .RepeatForever() ) .Build(); // Tell quartz to schedule the job using our trigger await scheduler.ScheduleJob(job, trigger); // and last shut down the scheduler when you are ready to close your program await scheduler.Shutdown();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40使用 quartz.config 配置文件时使用
StdSchedulerFactory()
构造函数。
注意:需要将该配置文件的 复制到输出目录 属性设置为 如果较新则复制 或 始终复制 。
示例如下:quartz.config
propertiesquartz.serializer.type = binary quartz.scheduler.instanceName = MyScheduler quartz.jobStore.type = Quartz.Impl.AdoJobStore.JobStoreTX, Quartz quartz.jobStore.driverDelegateType = Quartz.Impl.AdoJobStore.SqlServerDelegate, Quartz quartz.jobStore.tablePrefix = QRTZ_ quartz.jobStore.dataSource = myDS quartz.dataSource.myDS.connectionString = Data Source=LIUJIAJIA\LJJ2008;Initial Catalog=Quartz;User ID=sa;Password=Aa123456; quartz.dataSource.myDS.provider = SqlServer quartz.threadPool.threadCount = 3
1
2
3
4
5
6
7
8
9csharp// 加载当前目录下的 quartz.config 配置文件,若找不到则加载默认配置 StdSchedulerFactory factory = new StdSchedulerFactory(); IScheduler scheduler = await factory.GetScheduler(); // and start it off await scheduler.Start(); // define some jobs/triggers // and last shut down the scheduler when you are ready to close your program await scheduler.Shutdown();
1
2
3
4
5
6
7
8
9
10
11
集群
集群时仅能使用 AdoJobstore (JobStoreTX). 包含 负载均衡 和 故障转移(仅在 JobDetail
的 request recovery 设置为 true 时)功能。
通过将 quartz.jobStore.clustered
设置为 true
来启动集群功能。
集群中的每个实例应该使用相同的配置。
其中 quartz.threadPool.threadCount
和 quartz.scheduler.instanceId
是例外。quartz.threadPool.threadCount
可以不一致。quartz.scheduler.instanceId
在集群中则必须是唯一的。可以通过将 quartz.scheduler.instanceId
属性设置为 AUTO 来实现。该属性默认值为 NON_CLUSTERED 。
注意:禁止在不同的机器上开启集群,除非机器之间使用某种时间同步服务(误差必须在 1 秒以内)。
注意:不要在不是集群的实例间使用同一组数据表,否则会导致数据污染等问题。
插件
Quartz 提供了 ISchedulerPlugin
接口来加载附加功能。
具体可以参考 Quartz.Plugins
命名空间中的文档。
Quartz.Plugins 不包含在 Quartz 包中,需要单独安装。
Install-Package Quartz.Plugins
当前最新版是 3.0.7,提供了如下插件:
LoggingJobHistoryPlugin
Logs a history of all job executions (and execution vetoes) via common logging.
LoggingTriggerHistoryPlugin
Logs a history of all trigger firings via the Jakarta Commons-Logging framework.
ShutdownHookPlugin
This plugin catches the event of the VM terminating (such as upon a CRTL-C) and tells the scheduler to Shutdown.
XMLSchedulingDataProcessorPlugin
This plugin loads XML file(s) to add jobs and schedule them with triggers as the scheduler is initialized, and can optionally periodically scan the file for changes.
JobFactory
当 Trigger 被触发时,通过 Scheduler 中配置的 JobFactory 来实例化 Job class。默认的 JobFactory 只是简单的创建一个新的 Job class 实例。
如果需要在创建/初始化 Job 实例时实现额外的处理(如使用 IoC 或 DI)可以通过实现 IJobFactory
接口定义自己的 JobFactory 来实现。
通过 Scheduler.SetJobFactory(fact)
方法来设置自定义的 JobFactory。
Factory-Shipped’ Jobs
Quartz 提供了一下公用的 Job(如发送电子邮件或调用远程作业)。你可以在 Quartz.Jobs
命名空间下发现这些开箱即用的功能。
Quartz.Jobs 不包含在 Quartz 包中,需要单独安装。
Install-Package Quartz.Jobs
当前最新版是 3.0.7,提供了如下 Job:
DirectoryScanJob
Inspects a directory and compares whether any files' "last modified dates" have changed since the last time it was inspected.
If one or more files have been updated (or created), the job invokes a "call-back" method on an identifiedQuartz.Job.IDirectoryScanListener
that can be found in theQuartz.SchedulerContext
.FileScanJob
Inspects a file and compares whether it's "last modified date" has changed since the last time it was inspected.
If the file has been updated, the job invokes a "call-back" method on an identifiedQuartz.Job.IFileScanListener
that can be found in theQuartz.SchedulerContext
.NativeJob
Built in job for executing native executables in a separate process.
csharpJobDetail job = new JobDetail("dumbJob", null, typeof(Quartz.Jobs.NativeJob)); job.JobDataMap.Put(Quartz.Jobs.NativeJob.PropertyCommand, "echo \"hi\" >> foobar.txt"); Trigger trigger = TriggerUtils.MakeSecondlyTrigger(5); trigger.Name = "dumbTrigger"; sched.ScheduleJob(job, trigger);
1
2
3
4
5If PropertyWaitForProcess is true, then the integer exit value of the process will be saved as the job execution result in the JobExecutionContext.
NoOpJob
An implementation of Job, that does absolutely nothing - useful for system which only wish to use
Quartz.ITriggerListeners
andQuartz.IJobListeners
, rather than writing Jobs that perform work.SendMailJob
A Job which sends an e-mail with the configured content to the configured recipient.
quartz.serializer.type
这个配置项指定 Job/Trigger 中的数据保存到数据库时的序列化及反序列类型。
在使用非 RAMJobStore 时必须设置该配置,否则会报如下错误。可以选择的值为 binary 和 json。
Quartz.SchedulerException: You must define object serializer using configuration key 'quartz.serializer.type' when using other than RAMJobStore. Out of the box supported values are 'json' and 'binary'. JSON doesn't suffer from versioning as much as binary serialization but you cannot use it if you already have binary serialized data.
at Quartz.Impl.StdSchedulerFactory.Instantiate() in C:\projects\quartznet\src\Quartz\Impl\StdSchedulerFactory.cs:line 352
at Quartz.Impl.StdSchedulerFactory.GetScheduler(CancellationToken cancellationToken) in C:\projects\quartznet\src\Quartz\Impl\StdSchedulerFactory.cs:line 1114
at FirstQuartzNet.Program.RunProgram() in C:\Users\liujiajia\source\repos\FirstQuartzNet\FirstQuartzNet\Program.cs:line 45
2
3
4
如果设置为 json 需要安装 Quartz.Serialization.Json 包。
Install-Package Quartz.Serialization.Json