Quartz.NET 快速入门指南
欢迎来到 Quartz.NET 的快速入门指南。在阅读本指南的过程中,您将了解到以下内容:
- 如何下载 Quartz.NET
- 安装 Quartz.NET 的步骤
- 根据您的特定需求配置 Quartz
- 启动示例应用
下载与安装
您可以选择下载压缩文件或使用 NuGet 包。NuGet 包仅包含运行 Quartz.NET 所需的二进制文件,而压缩文件则包含了源代码、示例以及 Quartz.NET 服务器示例应用。
NuGet 包
没有比这更简单的了。只需启动已安装 NuGet 的 Visual Studio,然后从包管理器扩展中引用 Quartz 包:
- 右键点击项目中的“引用”,选择 管理 NuGet 包...
- 从左侧选择 在线 分类
- 在右上角搜索框输入 Quartz 并回车
- 从搜索结果中选择 Quartz.NET 并点击安装
- 完成!
或使用 NuGet 命令行工具:
Install-Package Quartz
如果您希望添加 JSON 序列化支持,同样方式添加 Quartz.Serialization.Json 包。
压缩包下载
简短版 :下载 Quartz.NET 后,解压到任意位置,从 bin
目录中取出 Quartz.dll
文件并开始使用它。
Quartz 核心库没有硬性的二进制依赖。当您选择使用 JSON 序列化包时,才需要 JSON.NET。为了让 Quartz.NET 成功运行,至少需要将 Quartz.dll
放在应用二进制文件旁边。因此,只需将其作为引用添加到使用它们的 Visual Studio 项目中。您可以在解压后的档案中,通过路径 bin\your-target-framework-version\release\Quartz 找到这些 dll 文件。
配置
这是关键环节!Quartz.NET 是一个高度可配置的库。提供 Quartz.NET 配置信息主要有两种方式(这两种方式并不互斥):
流畅调度器构建器 API
您可以使用 C# 的流畅 API 来配置调度器,或者通过向调度器工厂提供包含配置键值对的 NameValueCollection
参数来进行配置。
// 初始化一个基础属性集合,这里未展示具体添加属性的代码,但你可以在此处添加自定义的配置项
var properties = new NameValueCollection();
// 使用SchedulerBuilder创建调度器实例,并根据需要覆盖或添加配置
IScheduler scheduler = await SchedulerBuilder.Create(properties)
// 默认最大并发度为10,这里修改为5
.UseDefaultThreadPool(x => x.MaxConcurrency = 5)
// 以下注释掉的行表示默认情况下,作业错过触发时间的阈值为60秒,可以根据需要取消注释并调整
// .WithMisfireThreshold(TimeSpan.FromSeconds(60))
// 配置持久化存储策略
.UsePersistentStore(x =>
{
// 强制作业数据映射的值被视为字符串,避免对象意外序列化后格式破坏导致的问题,默认为false
x.UseProperties = true;
// 启用集群模式
x.UseClustering();
// 使用SQL Server作为持久化存储的提供者,并提供连接字符串
x.UseSqlServer("my connection string");
// 使用JSON序列化器,要求添加Quartz.Serialization.Json NuGet包
x.UseJsonSerializer();
})
// 配置XML作业初始化插件来处理XML作业定义文件,需要Quartz.Plugins NuGet包
.UseXmlSchedulingConfiguration(x =>
{
// 指定XML作业定义文件的位置,这里是相对路径
x.Files = new[] { "~/quartz_jobs.xml" };
// 如果找不到定义文件,默认行为是失败(此处保持默认)
x.FailOnFileNotFound = true;
// 当遇到作业调度错误时,默认不失败,这里修改为遇到错误时失败
x.FailOnSchedulingError = true;
})
// 最后,基于上述配置构建并返回调度器实例
.BuildScheduler();
// 启动调度器
await scheduler.Start();
配置文件
以下文件会被搜索以查找已知的配置属性:
YourApplication.exe.config
- 使用 quartz 元素的配置文件(仅限完整 .NET 框架)appsettings.json
- (.NET Core/NET5 及以后版本)quartz.config
- 应用程序根目录下的配置文件(同时适用于 .NET Core 和完整 .NET 框架)
所有可用属性的完整文档参见 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,是时候让一个示例应用程序跑起来了。下面的代码将获取调度器的一个实例,启动它,然后关闭它:
Program.cs
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!");
}
}
}
创建并初始化数据库
为了使用 SQL 持久化存储来支持 Quartz,并启用如集群等功能,你需要创建一个数据库并使用 SQL 脚本初始化模式对象。首先,你需要为 Quartz 创建一个数据库及凭证。在 Quartz 能够连接到的数据库准备好之后,你还需创建 Quartz 运行所需的数据表和索引。
最新的 DDL 脚本可以在 Quartz 的 GitHub 仓库(在新窗口中打开) 中找到,它们也包含在 ZIP 归档分发包中。还有第三方对 Quartz 的扩展,使得其能支持其他类型的存储,比如 NoSQL 数据库。你可以在 NuGet 上搜索它们。
现在,你可以开始探索 Quartz.NET 并享受乐趣了!你可以继续阅读 教程 深入学习。