Quartz.Net的基本使用方法
Quartz.Net的基本使用是比较简单的,主要是对下面三个工具的创建和使用。
- Scheduler调度器
- Job执行的动作
- Trigger触发器
Scheduler的创建和使用
scheduler的创建有几种不同的方式,但一般可以直接使用其提供的工厂类直接创建
- 通过工厂类创建-----像StdSchedulerFactory和DirectSchedulerFactory
/*StdFactory是用到最多的情况,亦可以通过NameValueCollection读取不同形式的键值对参数(.prop,.txt应该都可以),其创建的Scheduler即为StdScheduler类型*/
StdSchedulerFactory stdSchedulerFactory = new StdSchedulerFactory();
IScheduler scheduler = await stdSchedulerFactory.GetScheduler();
/*directfatory和stdfactory有很多区别,一般可以用于更细节的Custom,
1. directfactory为单例设计
2. directfactory可以通过CreateScheduler(args)自定scheduler
3. directfactory无法通过键值对获取初始属性 */
DirectSchedulerFactory directSchedulerFactory = DirectSchedulerFactory.Instance;
directSchedulerFactory.CreateScheduler(new DefaultThreadPool(), new RAMJobStore());
IScheduler scheduler2 = await directSchedulerFactory.GetScheduler();
- 绑定Job和Trigger
在scheduler里绑定对应的Job和Trigger,然后调用Start()开始即可。
static async Task Main(string[] args)
{
StdSchedulerFactory stdSchedulerFactory = new StdSchedulerFactory();
IScheduler scheduler = await stdSchedulerFactory.GetScheduler();
//这里先直接使用builder创建trigger,在后面再详细的解释(PS:withRepeateCount的实际执行次数为count+1)
ITrigger trigger = TriggerBuilder.Create().WithSimpleSchedule(action => { action.WithIntervalInSeconds(2).WithRepeatCount(1); }).WithIdentity("TriggerOne").Build();
//创建一个简单的Job,一般的Job都有很多的参数,这里先用无参的方法替代一下。
IJobDetail job = JobBuilder.Create<SimpleJob>().WithIdentity("JOB:one").Build();
await scheduler.ScheduleJob(job, trigger);
await scheduler.Start();
//停主线程--查看效果
Thread.Sleep(7000);
}
public class SimpleJob : IJob
{
public async Task Execute(IJobExecutionContext context)
{
Console.WriteLine("Hello QuartZnet!");
}
}
Job的创建和参数的传递
只要实现IJob接口的类就可以称之为Job类,
IJob接口只有Execute(IJobExecutionContext context)方法。
其context为上下文参数,保存着IJob执行过程的信息,
也保存着需要的参数。
方法体则为具体的定时业务
Job的传参方式
Job的传参方式为通过JobDetail的JobDataMap进行键值对存取。
static async Task Main(string[] args)
{
StdSchedulerFactory stdSchedulerFactory = new StdSchedulerFactory();
IScheduler scheduler = await stdSchedulerFactory.GetScheduler();
ITrigger trigger = TriggerBuilder.Create().WithSimpleSchedule(action => { action.WithIntervalInSeconds(2).WithRepeatCount(1); }).WithIdentity("TriggerOne").Build();
IJobDetail job = JobBuilder.Create<SimpleJob>().WithIdentity("JOB:one").Build();
//在键值对中添加参数
job.JobDataMap.Add("UserName", "小明");
job.JobDataMap.Add("Pets", new List<string>() { "小狗","小猫","乌龟","仓鼠" });
await scheduler.ScheduleJob(job, trigger);
await scheduler.Start();
Thread.Sleep(7000);
}
public class SimpleJob : IJob
{
public async Task Execute(IJobExecutionContext context)
{
Console.WriteLine("Hello QuartZnet!");
//再context上下文中获取参数[也可以获取执行的其他信息]
string argOne = (string)context.JobDetail.JobDataMap.Get("UserName");
List<string> argTwo = (List<string>)context.JobDetail.JobDataMap.Get("Pets");
Console.WriteLine(argOne);
argTwo.ForEach(item => { Console.WriteLine(item); });
}
}
PS: 不要在Job类中添加自定义的构造函数,因为JobBuilder是无法使用DI(依赖注入)配置Job的,故无法使用构造函数传参
Job的ID设置
当你需要创建不确定量的任务时,你需要设置Job的ID不唯一。即:
不能多个scheduler启动一个相同ID的Job.
错误例:
StdSchedulerFactory stdSchedulerFactory = new StdSchedulerFactory();
//假设有两个调度器,
IScheduler scheduler = await stdSchedulerFactory.GetScheduler();
IScheduler scheduler2 = await stdSchedulerFactory.GetScheduler();
ITrigger trigger = TriggerBuilder.Create().WithSimpleSchedule(action => { action.WithIntervalInSeconds(2).WithRepeatCount(1); }).WithIdentity("TriggerOne").Build();
IJobDetail job = JobBuilder.Create<SimpleJob>().WithIdentity("ThisOne").Build();
job.JobDataMap.Add("UserName", "小明");
job.JobDataMap.Add("Pets", new List<string>() { "小狗","小猫","乌龟","仓鼠" });
await scheduler.ScheduleJob(job, trigger);
;
await scheduler.Start();
//ObjectAlreadyExistsException,
//Unable to store Job: 'DEFAULT.ThisOne', because one already exists with this identification.'
//这里的异常即为JobID已经存在(或者说是重复)
await scheduler2.ScheduleJob(job, trigger);
await scheduler2.Start();
Thread.Sleep(7000);
ID重复的解决方法
- 设置不同的ID(trigger也需要设置)
//本测试在Main方法中,故直接创建不同的Job/Trigger对象使用不同的ID即可
//如果你提供方法/API来创建任务,推荐使用:
//UUID,数据库生成ID
//来保证不重复
IScheduler scheduler = await stdSchedulerFactory.GetScheduler();
IScheduler scheduler2 = await stdSchedulerFactory.GetScheduler();
ITrigger trigger = TriggerBuilder.Create().WithSimpleSchedule(action => { action.WithIntervalInSeconds(2).WithRepeatCount(1); }).WithIdentity("TriggerOne").Build();
ITrigger trigger2 = TriggerBuilder.Create().WithSimpleSchedule(action => { action.WithIntervalInSeconds(2).WithRepeatCount(1); }).WithIdentity("TriggerTwo").Build();
IJobDetail job = JobBuilder.Create<SimpleJob>().WithIdentity("ThisOne").Build();
IJobDetail job2 = JobBuilder.Create<SimpleJob>().WithIdentity("ThisTwo").Build();
job.JobDataMap.Add("UserName", "小明");
job.JobDataMap.Add("Pets", new List<string>() { "小狗","小猫","乌龟","仓鼠" });
job2.JobDataMap.Add("UserName", "小明");
job2.JobDataMap.Add("Pets", new List<string>() { "小狗", "小猫", "乌龟", "仓鼠" });
await scheduler.ScheduleJob(job, trigger);
await scheduler.Start();
await scheduler2.ScheduleJob(job2, trigger2);
await scheduler2.Start();
Thread.Sleep(7000);
- UnScheduleJob(trigger);解绑Trigger和Job
通过UnScheduleJob可以解绑trigger和Job
可以在任何时候解绑
但也有着不同的效果
- Start后立马解绑------Job不会被执行
StdSchedulerFactory stdSchedulerFactory = new StdSchedulerFactory();
//假设有两个调度器,
IScheduler scheduler = await stdSchedulerFactory.GetScheduler();
ITrigger trigger = TriggerBuilder.Create().WithSimpleSchedule(action => { action.WithIntervalInSeconds(2).WithRepeatCount(1); }).WithIdentity("TriggerOne").Build();
IJobDetail job = JobBuilder.Create<SimpleJob>().WithIdentity("ThisOne").Build();
job.JobDataMap.Add("UserName", "小明");
job.JobDataMap.Add("Pets", new List<string>() { "小狗","小猫","乌龟","仓鼠" });
await scheduler.ScheduleJob(job, trigger);
await scheduler.Start();
await scheduler.UnscheduleJob(new TriggerKey("TriggerOne"));
Thread.Sleep(6000);
执行效果:
-
schedule执行完毕后解绑-------同一Job和Trigger可以在解绑后再绑定其他schedule
StdSchedulerFactory stdSchedulerFactory = new StdSchedulerFactory(); //假设有两个调度器, IScheduler scheduler = await stdSchedulerFactory.GetScheduler(); IScheduler scheduler2 = await stdSchedulerFactory.GetScheduler(); ITrigger trigger = TriggerBuilder.Create().WithSimpleSchedule(action => { action.WithIntervalInSeconds(2).WithRepeatCount(1); }).WithIdentity("TriggerOne").Build(); IJobDetail job = JobBuilder.Create<SimpleJob>().WithIdentity("ThisOne").Build(); job.JobDataMap.Add("UserName", "小明"); job.JobDataMap.Add("Pets", new List<string>() { "小狗","小猫","乌龟","仓鼠" }); await scheduler.ScheduleJob(job, trigger); await scheduler.Start(); Thread.Sleep(6000); //等待执行完成后解绑job和trigger bool isCompleted = scheduler.GetTriggerState(new TriggerKey("TriggerOne")).IsCompleted; if(isCompleted) await scheduler.UnscheduleJob(new TriggerKey("TriggerOne")); //再绑定新的scheduler,则可以正常运行了 await scheduler2.ScheduleJob(job, trigger); await scheduler2.Start(); Thread.Sleep(7000);
对比
- 创建新ID的Job和Trigger
Job/Trigger间彼此独立,可以在同一时间创造多个任务,但是可能线程开销会比较大- 解绑Job和Trigger于scheduler,重复使用Job和Trigger
同一个Job可重复使用,可能可以节省开销,
但是如果解绑的时间可能和任务的执行时间造成冲突,导致任务无法执行
Trigger的创建和使用
Trigger为Job的触发器,通过TriggerBuilder进行创建
在QuartZ中,提供了4类现成的生成Trigger类型的方法
- WithSimpleSchedule
- WithDailyTimeIntervalSchedule
- WithCalendarIntervalSchedule
- WithCronSchedule
每一种方法都对应一些方面的安排
安排Trigger执行的开始/结束时间
可以通过StartAt和StartNow来控制开始时间
反之EndAt可以控制结束的时间。
static async Task Main(string[] args)
{
StdSchedulerFactory stdSchedulerFactory = new StdSchedulerFactory();
//假设有两个调度器,
IScheduler scheduler = await stdSchedulerFactory.GetScheduler();
//简单触发器
ITrigger simpleTrigger = TriggerBuilder.Create().WithSimpleSchedule(option => {
option.WithIntervalInSeconds(1); //可以设置间隔时间
option.WithRepeatCount(999); //也可以设置重复次数
})
.StartAt(DateTime.Now.AddSeconds(5)) //可以设置哎固定的区间内
.EndAt(DateTime.Now.AddSeconds(15)).Build();
IJobDetail jobDetail = JobBuilder.Create<SimpleJob>().WithIdentity("JobOne").Build();
await scheduler.ScheduleJob(jobDetail,simpleTrigger);
await scheduler.Start();
Thread.Sleep(15000);
}
public class SimpleJob : IJob
{
public async Task Execute(IJobExecutionContext context)
{
Console.WriteLine(DateTime.Now);
}
}
使用Cron表达式安排任务
CronTrigger的使用更加简单,只需要cron表达式作为参数即可,如果需要了解可以看下图
也有很多在线工具提供Cron表达式的生成
https://freeformatter.com/cron-expression-generator-quartz.html
例:
static async Task Main(string[] args)
{
StdSchedulerFactory stdSchedulerFactory = new StdSchedulerFactory();
IScheduler scheduler = await stdSchedulerFactory.GetScheduler();
//Cron表达式无法直接控制重复次数,但可以通过EndAt方法间接调节
ITrigger Crontrigger = TriggerBuilder.Create().WithCronSchedule("0/2 * * * * ?").StartAt(DateTime.Now.AddSeconds(5)).Build();
IJobDetail jobDetail = JobBuilder.Create<SimpleJob>().WithIdentity("JobOne").Build();
await scheduler.ScheduleJob(jobDetail, Crontrigger);
await scheduler.Start();
Thread.Sleep(15000);
}
public class SimpleJob : IJob
{
public async Task Execute(IJobExecutionContext context)
{
Console.WriteLine(DateTime.Now);
}
}