Quartz.NET是一个功能齐全的作业调度系统,可用于小型应用程序,也可用于大型企业系统。Quartz.NET是用C#编写的.NET库,移植于开源的Java作业调度框架Quartz。
Quartz简介
Quartz是一个作业调度系统,作业调度程序是一个负责在执行前执行(或者通知)其他软件组件的系统。
Quartz具有容错能力,可以在系统重启之间依然保持作业任务。
Quartz主要接口是Scheduler接口,它提供了简单的操作,如调度作业,启动/停止/暂停调度程序。
如果安排自己的软件组件执行,必须实现Job接口,执行Execute方法。
如果希望在触发时间前通知组件,必须实现TriggerListener或JobListener接口。
Quartz与Timer
.NET Framework具有内置计时器功能,System.Timers.Timer类,但与Quartz比较,不足之处:
- 定时器没有持久性机制
- 定时器灵活度低(仅能够设置开始时间和重复间隔)
- 定时器不使用线程池(每个定时器一个线程)
- 计时器没有真正的管理方案,需要自己编写机制
Quartz 3.X
Quartz使用
创建控制台应用程序,安装Quartz
Install-Package Quartz
创建自定义任务HelloJob
1
2
3
4
5
6
7
8
9
10
11namespace QuartzStart
{
public class HelloJob : IJob
{
public async Task Execute(IJobExecutionContext context)
{
await Console.Out.WriteLineAsync("Hello World");
}
}
}
//自定义任务HelloJOb,需要实现IJob接口,实现接口的唯一方法Excute设置记录日志ConsoleLogProvider
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
28namespace QuartzStart
{
public class ConsoleLogProvider : ILogProvider
{
public Logger GetLogger(string name)
{
return (level, func, exception, parameters) =>
{
if (level >= LogLevel.Info && func != null)
{
Console.WriteLine("[" + DateTime.Now.ToLongTimeString() + "] [" + level + "] " + func(), parameters);
}
return true;
};
}
public IDisposable OpenMappedContext(string key, string value)
{
throw new NotImplementedException();
}
public IDisposable OpenNestedContext(string message)
{
throw new NotImplementedException();
}
}
}
//Quartz默认的日志框架是LibLog,当然可以设置其他日志框架,如Log4NET,NLog和Serilog创建调度(Scheduler) 触发(Trigger)任务(JobDetail)
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
41
42
43
44
45
46
47
48
49
50
51
52
53namespace QuartzStart
{
class Program
{
public static void Main(string[] args)
{
//记录日志:使用默认日志框架LibLog
LogProvider.SetCurrentLogProvider(new ConsoleLogProvider());
//异步求值
RunProgram().GetAwaiter().GetResult();
Console.Read();
}
private static async Task RunProgram()
{
try
{
NameValueCollection props = new NameValueCollection {
{"quartz.serializer.type","binary"}
};
StdSchedulerFactory factory = new StdSchedulerFactory(props);
//通过工厂获取Scheduler实例
IScheduler scheduler = await factory.GetScheduler();
//启动调度Scheduler
await scheduler.Start();
//通过JobBuilder创建任务JobDetail实例,并与自定义任务HelloJob关连
IJobDetail job = JobBuilder.Create<HelloJob>().WithIdentity("job1", "group1").Build();
//通过TriggerBulider创建触发器实例Trigger,并设置立即触发任务,且每10秒重复一次
ITrigger trigger = TriggerBuilder.Create().WithIdentity("trigger1", "group1").StartNow()
.WithSimpleSchedule(x => x.WithIntervalInSeconds(10).RepeatForever()).Build();
//通过Trigger执行调度任务Job
await scheduler.ScheduleJob(job, trigger);
//等待一分钟
await Task.Delay(TimeSpan.FromSeconds(60));
//关闭调度 当你关闭应用程序
await scheduler.Shutdown();
}
catch (SchedulerException se)
{
Console.WriteLine(se);
}
}
}
}运行结果
Job和Trigger
Quartz API关键的接口和类:
- IScheduler:与调度程序交互的主要接口
- IJob:调度程序执行的任务实现的接口
- IJobDetail:定义任务实例接口
- ITrigger:定义执行任务的触发器(计划)
- JobBuilder:创建JobDetail实例
- TriggerBuilder:创建触发器实例
Job和JobDetail
自定义的任务类型需要实现IJob接口,通过JobBuilder创建JobDetail实例。
JobBuilder每次触发都会创建新的实例,这就要求:
- 自定义任务类具有一个无参的构造函数
- 在任务类上定义数据字段没有意义,它们的值不会在作业之间保留
如果想为Job实例提供属性或者执行作业跟踪作业状态,就要用到JobDataMap。
JobDataMap可用于保存在作业实例执行时可用的任意数量的(可序列化)对象。JobDataMap是IDictionary接口的一个实现。注意:JobDataMap存入复杂对象,序列化可能出现类版本问题。
JobDataMap设置值
1
2
3
4
5//为JobDetail的JobDataMap属性添加数据
IJobDetail job = JobBuilder.Create<HelloJob>().WithIdentity("job1", "group1")
.UsingJobData("jobSay", "HelloWorld")
.UsingJobData("myAge", 18)
.Build();JobDataMap获取值
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
32namespace QuartzStart
{
public class HelloJob : IJob
{
public async Task Execute(IJobExecutionContext context)
{
JobKey key = context.JobDetail.Key;
//读取JobDetail属性JobDataMap存储的值
JobDataMap dataMap = context.JobDetail.JobDataMap;
string jobSay = dataMap.GetString("jobSay");
int myAge = dataMap.GetInt("myAge");
await Console.Out.WriteLineAsync("Instance " + key + " of HelloJob says: " + jobSay + ", and age is: " + myAge);
}
}
}
//在作业类中添加与JobDataMap键名称相对应的属性,则Quartz在实现作业类实例化时会自动为属性赋值。
namespace QuartzStart
{
public class HelloJob : IJob
{
//同名属性首字母大写
public string JobSay { private get; set; }
public int MyAge { private get; set; }
public async Task Execute(IJobExecutionContext context)
{
JobKey key = context.JobDetail.Key;
await Console.Out.WriteLineAsync("Instance " + key + " of HelloJob says: " + JobSay + ", and age is: " + MyAge);
}
}
}s运行结果
Trigger
- TriggerKey:触发器身份
- Prioroty:触发器优先级
- Calendars:实现ICalendar接口的类可以与触发器关连
SimpleTrigger
- 特定时刻执行一次作业,如22点49分触发
- 特定时刻执行作业,然后按特定时间间隔重复,如10秒触发一次
- 开始时间:StartAt
- 结束时间:EndAt
- 重复计数:WithRepeatCount
- 重复间隔:WithIntervalIn….
1 | //1.在当前时间触发一次 |
CronTrigger
基于日历设置作业计划,如每周五上午9:00
- 开始时间
- 结束时间
- Corn表达式
- Corn表达式由七个子表达式组成,用空格分隔
- 秒 分 时 日 月 周 年,如0 0 12 ? * WED 表示“每周三中午12点”
1 | //每天23点到0点之间,每隔一分钟触发一次 |
TriggerListeners
接收与触发器相关的事件,可以创建自己的监听器,通过实现ITriggerListener接口,方便起见,可以扩展TriggerListenerSupport类,覆盖相应的事件
1 | public interface ITriggerListener |
JobListeners
接收与作业相关的事件,可以创建自己的监听器,通过实现IJobListener接口,方便起见,可以扩展JobListenerSupport类,覆盖相应的事件
1 | public interface IJobListener |
SchedulerListeners
接收调度程序相关的事件,SchedulerListeners在ListenerManager中注册。
- 新增SchedulerListener:
scheduler.ListenerManager.AddSchedulerListener(mySchedListener);
- 删除SchedulerListener:
scheduler.ListenerManager.RemoveSchedulerListener(mySchedListener);
JobStores
负责根据你为调度程序提供的所有工作数据:作业、触发器、日历等,切勿在代码中直接使用JobStore实例。
RAMJobStore
quartz.jobStore.type = Quartz.Simpl.RAMJobStore, Quartz
ADO.NET JobStore(ADOJobStore)
quartz.jobStore.type = Quartz.Impl.AdoJobStore.JobStoreTX, Quartz
最后更新: 2018年09月29日 00:06
原始链接: https://www.github.com/ChangHub/BlogImages/raw/master/2018/09/25/作业调度Quartz.NET/