Quartz.NET是一个强大、开源、轻量的作业调度框架,你能够用它来为执行一个作业而创建简单的或复杂的作业调度。Quartz.NET 3.0支持了.NET Core 和async/await。
官网:http://www.quartz-scheduler.net/
源码:https://github.com/quartznet/quartznet
示例:https://www.quartz-scheduler.net/documentation/quartz-3.x/quick-start.html
废话不多说直接上代码
nuget引用Quartz 3.0
- 用代码调度定时任务
public async static Task Init()
{
LogProvider.SetCurrentLogProvider(new CustomConsoleLogProvider());//Quartz提供了日志接口,这里可以自定义日志
#region Scheduler 调度器
StdSchedulerFactory factory = new StdSchedulerFactory();
IScheduler scheduler = await factory.GetScheduler();//获取调度器
scheduler.ListenerManager.AddSchedulerListener(new CustomSchedulerListener());//添加调度器监听
scheduler.ListenerManager.AddTriggerListener(new CustomTriggerListener());//添加时间策略监听
scheduler.ListenerManager.AddJobListener(new CustomJobListener());//添加作业监听
await scheduler.Start();//启动调度器
#endregion
#region 创建Job(作业)
IJobDetail jobDetail = JobBuilder.Create<TestJob>()
.WithIdentity("testjob", "group1")//testjob- 作业名称 group1-分组名称
.WithDescription("This is TestJob jobDetail")//添加描述
.Build();
jobDetail.JobDataMap.Add("key1", "value1");
jobDetail.JobDataMap.Add("key2", "value2");
jobDetail.JobDataMap.Add("key3", "value3");
#endregion
# region 创建时间策略 ITrigger
ITrigger trigger = TriggerBuilder.Create()
.WithIdentity("testtrigger1", "group1")//testtrigger1- 时间策略名称 group1-分组名称
.StartAt(new DateTimeOffset(DateTime.Now.AddSeconds(10)))//10秒后启动
//.StartNow() //马上启动
.WithCronSchedule("5/10 * * * * ?")//每隔10秒执行一次
.WithDescription("This is testjob's Trigger")//添加描述
.Build();
#endregion
await scheduler.ScheduleJob(jobDetail,trigger);//按时间策略调度作业
}
public class CustomConsoleLogProvider : ILogProvider
{
public Logger GetLogger(string name)
{
return new Logger((level, func, exception, parameters) =>
{
if (level >= LogLevel.Info && func != null)
{
Console.WriteLine($"[{ DateTime.Now.ToLongTimeString()}] [{ level}] { func()} {string.Join(";", parameters.Select(p => p == null ? " " : p.ToString()))} 自定义日志{name}");
}
return true;
});
}
public IDisposable OpenMappedContext(string key, string value)
{
throw new NotImplementedException();
}
public IDisposable OpenNestedContext(string message)
{
throw new NotImplementedException();
}
}
public class CustomSchedulerListener : ISchedulerListener
{
public async Task JobAdded(IJobDetail jobDetail, CancellationToken cancellationToken = default)
{
await Task.Run(() =>
{
Console.WriteLine($" {DateTime.Now} {nameof(JobAdded)} {Thread.CurrentThread.ManagedThreadId} {jobDetail.Description}");
});
}
public Task JobDeleted(JobKey jobKey, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task JobInterrupted(JobKey jobKey, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task JobPaused(JobKey jobKey, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task JobResumed(JobKey jobKey, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public async Task JobScheduled(ITrigger trigger, CancellationToken cancellationToken = default)
{
await Task.Run(() =>
{
Console.WriteLine($" {DateTime.Now} {nameof(JobScheduled)} {Thread.CurrentThread.ManagedThreadId} {trigger.Description}");
});
}
public Task JobsPaused(string jobGroup, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task JobsResumed(string jobGroup, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task JobUnscheduled(TriggerKey triggerKey, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task SchedulerError(string msg, SchedulerException cause, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task SchedulerInStandbyMode(CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task SchedulerShutdown(CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task SchedulerShuttingdown(CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public async Task SchedulerStarted(CancellationToken cancellationToken = default)
{
await Task.Run(() =>
{
Console.WriteLine($" {DateTime.Now} {nameof(SchedulerStarted)} {Thread.CurrentThread.ManagedThreadId} {cancellationToken.WaitHandle.SafeWaitHandle.GetHashCode()}");
});
}
public async Task SchedulerStarting(CancellationToken cancellationToken = default)
{
await Task.Run(() =>
{
Console.WriteLine($" {DateTime.Now} {nameof(SchedulerStarting)} {Thread.CurrentThread.ManagedThreadId} {cancellationToken.WaitHandle.SafeWaitHandle.GetHashCode()}");
});
}
public Task SchedulingDataCleared(CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task TriggerFinalized(ITrigger trigger, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task TriggerPaused(TriggerKey triggerKey, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task TriggerResumed(TriggerKey triggerKey, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task TriggersPaused(string triggerGroup, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public Task TriggersResumed(string triggerGroup, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
}
public class CustomJobListener : IJobListener
{
public string Name => nameof(CustomJobListener);
public async Task JobExecutionVetoed(IJobExecutionContext context, CancellationToken cancellationToken = default(CancellationToken))
{
await Task.Run(() => {
Console.WriteLine($"CustomJobListener JobExecutionVetoed {context.JobDetail.Description}");
});
}
public async Task JobToBeExecuted(IJobExecutionContext context, CancellationToken cancellationToken = default(CancellationToken))
{
await Task.Run(() => {
Console.WriteLine($"CustomJobListener JobToBeExecuted {context.JobDetail.Description}");
});
}
public async Task JobWasExecuted(IJobExecutionContext context, JobExecutionException jobException, CancellationToken cancellationToken = default(CancellationToken))
{
await Task.Run(() => {
Console.WriteLine($"CustomJobListener JobWasExecuted {context.JobDetail.Description}");
});
}
}
public class CustomTriggerListener : ITriggerListener
{
public string Name =>nameof(CustomTriggerListener);
public async Task TriggerComplete(ITrigger trigger, IJobExecutionContext context, SchedulerInstruction triggerInstructionCode, CancellationToken cancellationToken = default)
{
await Task.Run(() =>
{
Console.WriteLine($"CustomTriggerListener TriggerComplete {trigger.Description}");
});
}
public async Task TriggerFired(ITrigger trigger, IJobExecutionContext context, CancellationToken cancellationToken = default)
{
await Task.Run(() =>
{
Console.WriteLine($"CustomTriggerListener TriggerFired {trigger.Description}");
});
}
public async Task TriggerMisfired(ITrigger trigger, CancellationToken cancellationToken = default)
{
await Task.Run(() =>
{
Console.WriteLine($"CustomTriggerListener TriggerMisfired {trigger.Description}");
});
}
public async Task<bool> VetoJobExecution(ITrigger trigger, IJobExecutionContext context, CancellationToken cancellationToken = default)
{
await Task.Run(() =>
{
Console.WriteLine($"CustomTriggerListener TriggerMisfired {trigger.Description}");
});
return false;//false才能继续执行
}
}
public class TestJob : IJob
{
public async Task Execute(IJobExecutionContext context)
{
await Task.Run(() =>
{
//需要执行的方法
Console.WriteLine($"{nameof(TestJob)} {Thread.CurrentThread.ManagedThreadId} {DateTime.Now}");
});
}
}
- 结合Topshelf配置文件调度定时任务
HostFactory.Run(x =>
{
x.Service<ServiceRunner>();
x.EnablePauseAndContinue();
});
public sealed class ServiceRunner : ServiceControl, ServiceSuspend
{
private readonly IScheduler scheduler;
public ServiceRunner()
{
scheduler = StdSchedulerFactory.GetDefaultScheduler().Result;
scheduler.ListenerManager.AddSchedulerListener(new CustomSchedulerListener());//添加调度器监听
}
public bool Start(HostControl hostControl)
{
scheduler.Start();
return true;
}
public bool Stop(HostControl hostControl)
{
scheduler.Shutdown(false);
return true;
}
public bool Continue(HostControl hostControl)
{
scheduler.ResumeAll();
return true;
}
public bool Pause(HostControl hostControl)
{
scheduler.PauseAll();
return true;
}
}
quartz.config
# You can configure your scheduler in either <quartz> configuration section
# or in quartz properties file
# Configuration section has precedence
quartz.scheduler.instanceName = QuartzTest
# configure thread pool info
quartz.threadPool.type = Quartz.Simpl.SimpleThreadPool, Quartz
quartz.threadPool.threadCount = 10
quartz.threadPool.threadPriority = Normal
# job initialization plugin handles our xml reading, without it defaults are used
quartz.plugin.xml.type = Quartz.Plugin.Xml.XMLSchedulingDataProcessorPlugin, Quartz.Plugins
quartz.plugin.xml.fileNames = ~/quartz_jobs.xml
# export this server to remoting context
#quartz.scheduler.exporter.type = Quartz.Simpl.RemotingSchedulerExporter, Quartz
#quartz.scheduler.exporter.port = 555
#quartz.scheduler.exporter.bindName = QuartzScheduler
#quartz.scheduler.exporter.channelType = tcp
#quartz.scheduler.exporter.channelName = httpQuartz
# 添加Job(作业)监听
quartz.jobListener.CustomJobListener.type=TestConsole.QuartzHelper.CustomJobListener,TestConsole
# 添加Trigger(时间策略)监听
quartz.triggerListener.CustomTriggerListener.type=TestConsole.QuartzHelper.CustomTriggerListener,TestConsole
quartz_jobs.xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- This file contains job definitions in schema version 2.0 format -->
<job-scheduling-data xmlns="http://quartznet.sourceforge.net/JobSchedulingData" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.0">
<processing-directives>
<overwrite-existing-data>true</overwrite-existing-data>
</processing-directives>
<schedule>
<job>
<name>TestJob</name>
<group>TestJob</group>
<description>TestJob</description>
<job-type>TestConsole.QuartzHelper.TestJob,TestConsole</job-type>
<durable>true</durable>
<recover>false</recover>
</job>
<trigger>
<cron>
<name>TestJobTrigger</name>
<group>TestJob</group>
<job-name>TestJob</job-name>
<job-group>TestJob</job-group>
<start-time>2017-01-25T00:00:00+08:00</start-time>
<cron-expression>0/10 * * * * ? </cron-expression>
</cron>
</trigger>
</schedule>
</job-scheduling-data>
使用配置文件需要注意的是nuget需要引入Quartz.Plugins
本文参考文档:
- Quartz.NET 3.0 :https://www.cnblogs.com/shanyou/p/8269641.html
- Topshelf:https://www.cnblogs.com/abeam/p/8032080.html
- Topshelf 结合 Quartz.NET:https://www.cnblogs.com/abeam/p/8042531.html
- Quartz.NET 配置文件详解:https://www.cnblogs.com/abeam/p/8044460.html