欢迎使用 Quartz.NET 快速入门指南。当您阅读本指南时,希望看到以下详细信息:
- 下载 Quartz.NET
- 安装 Quartz.NET
- 根据您自己的特定需求配置 Quartz
- 启动示例应用程序
下载并安装
您可以下载 zip 文件或使用 NuGet 包。NuGet 包仅包含运行 Quartz.NET 所需的二进制文件,zip 文件附带源代码、示例和 Quartz.NET 服务器示例应用程序。
NuGet 包
没有比这更简单的了。只需启动 Visual Studio(安装了 NuGet)并从包管理器扩展中添加对包 Quartz 的引用:
- 右键单击项目的引用并选择 管理 NuGet 程序包…
- 从左侧选择 浏览 类别
- 输入 Quartz 到右上角搜索并回车
- 从搜索结果中选择 Quartz.NET 并点击安装
- 完毕!
或从 NuGet 命令行:
Install-Package Quartz
如果要添加 JSON 序列化,只需以相同的方式添加 Quartz.Serialization.Json 包。
Zip 压缩文档
简短版:下载 Quartz.NET 后,将其解压缩到某个位置,从 bin 目录中获取 Quartz.dll
并开始使用它。
Quartz 核心库没有任何硬二进制依赖。
当您选择使用需要 JSON.NET 的 JSON 序列化包时,您可以选择加入更多依赖项。
您至少需要在您的应用程序二进制文件旁边有 Quartz.dll
才能成功运行 Quartz.NET。
因此,只需将其添加为对使用它们的 Visual Studio 项目的引用。
您可以从路径 bin\your-target-framework-version\release\Quartz 提取的存档中找到这些 dll。
配置
这是个大问题! Quartz.NET 是一个非常可配置的库。 提供 Quartz.NET 配置信息的主要方式有两种(并不相互排斥):
Fluent Scheduler Builder API
您可以使用 C# fluent API 配置调度程序,或者通过向调度程序工厂提供包含配置键和值的 NameValueCollection
参数。
// you can have base properties
var properties = new NameValueCollection();
// and override values via builder
IScheduler scheduler = await SchedulerBuilder.Create(properties)
// default max concurrency is 10
.UseDefaultThreadPool(x => x.MaxConcurrency = 5)
// this is the default
// .WithMisfireThreshold(TimeSpan.FromSeconds(60))
.UsePersistentStore(x =>
{
// force job data map values to be considered as strings
// prevents nasty surprises if object is accidentally serialized and then
// serialization format breaks, defaults to false
x.UseProperties = true;
x.UseClustering();
// there are other SQL providers supported too
x.UseSqlServer("my connection string");
// this requires Quartz.Serialization.Json NuGet package
x.UseJsonSerializer();
})
// job initialization plugin handles our xml reading, without it defaults are used
// requires Quartz.Plugins NuGet package
.UseXmlSchedulingConfiguration(x =>
{
x.Files = new[] { "~/quartz_jobs.xml" };
// this is the default
x.FailOnFileNotFound = true;
// this is not the default
x.FailOnSchedulingError = true;
})
.BuildScheduler();
await scheduler.Start();
配置文件
搜索以下文件以查找已知配置属性:
- 使用 quartz-element 的
YourApplication.exe.config
配置文件(仅限完整的 .NET Framework) appsettings.json
(.NET Core/NET5 以上)- 应用程序根目录中的
quartz.config
文件(适用于 .NET Core 和完整的 .NET Framework)
Quartz 配置参考中提供了可用属性的完整文档。
为了快速启动和运行,一个基本的 quartz.config 看起来像这样:
quartz.scheduler.instanceName = MyScheduler
quartz.jobStore.type = Quartz.Simpl.RAMJobStore, Quartz
quartz.threadPool.maxConcurrency = 3
请记住在 Visual Studio 的文件属性页上将 复制到输出目录 设置为 始终复制。
否则,如果它不在构建目录中,则不会看到配置。
此配置创建的调度程序具有以下特点:
quartz.scheduler.instanceName
- 这个调度器的名字是“MyScheduler”。quartz.threadPool.maxConcurrency
- 最多可以同时运行 3 个作业(默认为 10)。quartz.jobStore.type
- Quartz 的所有数据,例如作业和触发器的详细信息,都保存在内存中(而不是数据库中)。即使您有一个数据库并且想将它与 Quartz 一起使用,我建议您在使用数据库打开一个全新的维度之前让 Quartz 与 RamJobStore 一起使用。
提示
实际上你不需要定义这些属性,如果你不想的话,Quartz.NET 带有健全的默认值
启动示例应用程序
现在您已经下载并安装了 Quartz,是时候启动并运行示例应用程序了。
以下代码获取调度程序的一个实例,启动它,然后关闭它:
using System;
using System.Threading.Tasks;
using Quartz;
using Quartz.Impl;
namespace QuartzSampleApp
{
public class Program
{
private static async Task Main(string[] args)
{
// Grab the Scheduler instance from the Factory
StdSchedulerFactory factory = new StdSchedulerFactory();
IScheduler scheduler = await factory.GetScheduler();
// and start it off
await scheduler.Start();
// some sleep to show what's happening
await Task.Delay(TimeSpan.FromSeconds(10));
// and last shut down the scheduler when you are ready to close your program
await scheduler.Shutdown();
}
}
}
从 Quartz 3.0 开始,当在 scheduler.Shutdown()
之后没有代码可以执行时,您的应用程序将终止,因为不会有任何活动线程。 如果您希望调度程序在 Task.Delay 和 Shutdown 处理完毕后也继续运行,您应该手动阻止应用程序的退出。
现在运行程序不会显示任何内容。 当 10 秒过去时,程序将终止。 让我们添加一些日志到控制台。
添加日志记录
LibLog 可以配置为在后台使用不同的日志框架; 即 Log4Net、NLog 和 Serilog。
当 LibLog 没有检测到任何其他日志框架存在时,它将保持沉默。 如果您还没有准备好日志框架设置,我们可以配置一个自定义记录器提供程序,它只记录到控制台显示输出。
LogProvider.SetCurrentLogProvider(new ConsoleLogProvider());
private 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 OpenNestedContext(string message)
{
throw new NotImplementedException();
}
public IDisposable OpenMappedContext(string key, object value, bool destructure = false)
{
throw new NotImplementedException();
}
}
试用应用程序并添加工作
现在我们应该在启动应用程序时获得更多信息。
[12.51.10] [Info] Quartz.NET properties loaded from configuration file 'C:\QuartzSampleApp\quartz.config'
[12.51.10] [Info] Initialized Scheduler Signaller of type: Quartz.Core.SchedulerSignalerImpl
[12.51.10] [Info] Quartz Scheduler created
[12.51.10] [Info] RAMJobStore initialized.
[12.51.10] [Info] Scheduler meta-data: Quartz Scheduler (v3.0.0.0) 'MyScheduler' with instanceId 'NON_CLUSTERED'
Scheduler class: 'Quartz.Core.QuartzScheduler' - running locally.
NOT STARTED.
Currently in standby mode.
Number of jobs executed: 0
Using thread pool 'Quartz.Simpl.DefaultThreadPool' - with 3 threads.
Using job-store 'Quartz.Simpl.RAMJobStore' - which does not support persistence. and is not clustered.
[12.51.10] [Info] Quartz scheduler 'MyScheduler' initialized
[12.51.10] [Info] Quartz scheduler version: 3.0.0.0
[12.51.10] [Info] Scheduler MyScheduler_$_NON_CLUSTERED started.
我们需要一个简单的测试作业来测试功能,让我们创建向控制台输出问候语的 HelloJob。
public class HelloJob : IJob
{
public async Task Execute(IJobExecutionContext context)
{
await Console.Out.WriteLineAsync("Greetings from HelloJob!");
}
}
要做一些有趣的事情,您需要在 Start() 方法之后、Task.Delay 之前的代码。
// define the job and tie it to our HelloJob class
IJobDetail job = JobBuilder.Create<HelloJob>()
.WithIdentity("job1", "group1")
.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);
// You could also schedule multiple triggers for the same job with
// await scheduler.ScheduleJob(job, new List<ITrigger>() { trigger1, trigger2 }, replace: true);
完整的控制台应用程序现在看起来像这样
using System;
using System.Threading.Tasks;
using Quartz;
using Quartz.Impl;
using Quartz.Logging;
namespace QuartzSampleApp
{
public class Program
{
private static async Task Main(string[] args)
{
LogProvider.SetCurrentLogProvider(new ConsoleLogProvider());
// Grab the Scheduler instance from the Factory
StdSchedulerFactory factory = new StdSchedulerFactory();
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")
.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);
// some sleep to show what's happening
await Task.Delay(TimeSpan.FromSeconds(60));
// and last shut down the scheduler when you are ready to close your program
await scheduler.Shutdown();
Console.WriteLine("Press any key to close the application");
Console.ReadKey();
}
// simple log provider to get something to the console
private 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 OpenNestedContext(string message)
{
throw new NotImplementedException();
}
public IDisposable OpenMappedContext(string key, object value, bool destructure = false)
{
throw new NotImplementedException();
}
}
}
public class HelloJob : IJob
{
public async Task Execute(IJobExecutionContext context)
{
await Console.Out.WriteLineAsync("Greetings from HelloJob!");
}
}
}
现在去探索 Quartz.NET 吧! 您可以继续阅读教程。