欢迎使用 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 命令行:

  1. 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 参数。

  1. // you can have base properties
  2. var properties = new NameValueCollection();
  3. // and override values via builder
  4. IScheduler scheduler = await SchedulerBuilder.Create(properties)
  5. // default max concurrency is 10
  6. .UseDefaultThreadPool(x => x.MaxConcurrency = 5)
  7. // this is the default
  8. // .WithMisfireThreshold(TimeSpan.FromSeconds(60))
  9. .UsePersistentStore(x =>
  10. {
  11. // force job data map values to be considered as strings
  12. // prevents nasty surprises if object is accidentally serialized and then
  13. // serialization format breaks, defaults to false
  14. x.UseProperties = true;
  15. x.UseClustering();
  16. // there are other SQL providers supported too
  17. x.UseSqlServer("my connection string");
  18. // this requires Quartz.Serialization.Json NuGet package
  19. x.UseJsonSerializer();
  20. })
  21. // job initialization plugin handles our xml reading, without it defaults are used
  22. // requires Quartz.Plugins NuGet package
  23. .UseXmlSchedulingConfiguration(x =>
  24. {
  25. x.Files = new[] { "~/quartz_jobs.xml" };
  26. // this is the default
  27. x.FailOnFileNotFound = true;
  28. // this is not the default
  29. x.FailOnSchedulingError = true;
  30. })
  31. .BuildScheduler();
  32. await scheduler.Start();

配置文件

搜索以下文件以查找已知配置属性:

  • 使用 quartz-element 的 YourApplication.exe.config 配置文件(仅限完整的 .NET Framework)
  • appsettings.json(.NET Core/NET5 以上)
  • 应用程序根目录中的 quartz.config 文件(适用于 .NET Core 和完整的 .NET Framework)

Quartz 配置参考中提供了可用属性的完整文档。

为了快速启动和运行,一个基本的 quartz.config 看起来像这样:

  1. quartz.scheduler.instanceName = MyScheduler
  2. quartz.jobStore.type = Quartz.Simpl.RAMJobStore, Quartz
  3. 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,是时候启动并运行示例应用程序了。
以下代码获取调度程序的一个实例,启动它,然后关闭它:

  1. using System;
  2. using System.Threading.Tasks;
  3. using Quartz;
  4. using Quartz.Impl;
  5. namespace QuartzSampleApp
  6. {
  7. public class Program
  8. {
  9. private static async Task Main(string[] args)
  10. {
  11. // Grab the Scheduler instance from the Factory
  12. StdSchedulerFactory factory = new StdSchedulerFactory();
  13. IScheduler scheduler = await factory.GetScheduler();
  14. // and start it off
  15. await scheduler.Start();
  16. // some sleep to show what's happening
  17. await Task.Delay(TimeSpan.FromSeconds(10));
  18. // and last shut down the scheduler when you are ready to close your program
  19. await scheduler.Shutdown();
  20. }
  21. }
  22. }

从 Quartz 3.0 开始,当在 scheduler.Shutdown() 之后没有代码可以执行时,您的应用程序将终止,因为不会有任何活动线程。 如果您希望调度程序在 Task.Delay 和 Shutdown 处理完毕后也继续运行,您应该手动阻止应用程序的退出。

现在运行程序不会显示任何内容。 当 10 秒过去时,程序将终止。 让我们添加一些日志到控制台。

添加日志记录

LibLog 可以配置为在后台使用不同的日志框架; 即 Log4Net、NLog 和 Serilog。

当 LibLog 没有检测到任何其他日志框架存在时,它将保持沉默。 如果您还没有准备好日志框架设置,我们可以配置一个自定义记录器提供程序,它只记录到控制台显示输出。

  1. LogProvider.SetCurrentLogProvider(new ConsoleLogProvider());
  2. private class ConsoleLogProvider : ILogProvider
  3. {
  4. public Logger GetLogger(string name)
  5. {
  6. return (level, func, exception, parameters) =>
  7. {
  8. if (level >= LogLevel.Info && func != null)
  9. {
  10. Console.WriteLine("[" + DateTime.Now.ToLongTimeString() + "] [" + level + "] " + func(), parameters);
  11. }
  12. return true;
  13. };
  14. }
  15. public IDisposable OpenNestedContext(string message)
  16. {
  17. throw new NotImplementedException();
  18. }
  19. public IDisposable OpenMappedContext(string key, object value, bool destructure = false)
  20. {
  21. throw new NotImplementedException();
  22. }
  23. }

试用应用程序并添加工作

现在我们应该在启动应用程序时获得更多信息。

  1. [12.51.10] [Info] Quartz.NET properties loaded from configuration file 'C:\QuartzSampleApp\quartz.config'
  2. [12.51.10] [Info] Initialized Scheduler Signaller of type: Quartz.Core.SchedulerSignalerImpl
  3. [12.51.10] [Info] Quartz Scheduler created
  4. [12.51.10] [Info] RAMJobStore initialized.
  5. [12.51.10] [Info] Scheduler meta-data: Quartz Scheduler (v3.0.0.0) 'MyScheduler' with instanceId 'NON_CLUSTERED'
  6. Scheduler class: 'Quartz.Core.QuartzScheduler' - running locally.
  7. NOT STARTED.
  8. Currently in standby mode.
  9. Number of jobs executed: 0
  10. Using thread pool 'Quartz.Simpl.DefaultThreadPool' - with 3 threads.
  11. Using job-store 'Quartz.Simpl.RAMJobStore' - which does not support persistence. and is not clustered.
  12. [12.51.10] [Info] Quartz scheduler 'MyScheduler' initialized
  13. [12.51.10] [Info] Quartz scheduler version: 3.0.0.0
  14. [12.51.10] [Info] Scheduler MyScheduler_$_NON_CLUSTERED started.

我们需要一个简单的测试作业来测试功能,让我们创建向控制台输出问候语的 HelloJob。

  1. public class HelloJob : IJob
  2. {
  3. public async Task Execute(IJobExecutionContext context)
  4. {
  5. await Console.Out.WriteLineAsync("Greetings from HelloJob!");
  6. }
  7. }

要做一些有趣的事情,您需要在 Start() 方法之后、Task.Delay 之前的代码。

  1. // define the job and tie it to our HelloJob class
  2. IJobDetail job = JobBuilder.Create<HelloJob>()
  3. .WithIdentity("job1", "group1")
  4. .Build();
  5. // Trigger the job to run now, and then repeat every 10 seconds
  6. ITrigger trigger = TriggerBuilder.Create()
  7. .WithIdentity("trigger1", "group1")
  8. .StartNow()
  9. .WithSimpleSchedule(x => x
  10. .WithIntervalInSeconds(10)
  11. .RepeatForever())
  12. .Build();
  13. // Tell quartz to schedule the job using our trigger
  14. await scheduler.ScheduleJob(job, trigger);
  15. // You could also schedule multiple triggers for the same job with
  16. // await scheduler.ScheduleJob(job, new List<ITrigger>() { trigger1, trigger2 }, replace: true);

完整的控制台应用程序现在看起来像这样

  1. using System;
  2. using System.Threading.Tasks;
  3. using Quartz;
  4. using Quartz.Impl;
  5. using Quartz.Logging;
  6. namespace QuartzSampleApp
  7. {
  8. public class Program
  9. {
  10. private static async Task Main(string[] args)
  11. {
  12. LogProvider.SetCurrentLogProvider(new ConsoleLogProvider());
  13. // Grab the Scheduler instance from the Factory
  14. StdSchedulerFactory factory = new StdSchedulerFactory();
  15. IScheduler scheduler = await factory.GetScheduler();
  16. // and start it off
  17. await scheduler.Start();
  18. // define the job and tie it to our HelloJob class
  19. IJobDetail job = JobBuilder.Create<HelloJob>()
  20. .WithIdentity("job1", "group1")
  21. .Build();
  22. // Trigger the job to run now, and then repeat every 10 seconds
  23. ITrigger trigger = TriggerBuilder.Create()
  24. .WithIdentity("trigger1", "group1")
  25. .StartNow()
  26. .WithSimpleSchedule(x => x
  27. .WithIntervalInSeconds(10)
  28. .RepeatForever())
  29. .Build();
  30. // Tell quartz to schedule the job using our trigger
  31. await scheduler.ScheduleJob(job, trigger);
  32. // some sleep to show what's happening
  33. await Task.Delay(TimeSpan.FromSeconds(60));
  34. // and last shut down the scheduler when you are ready to close your program
  35. await scheduler.Shutdown();
  36. Console.WriteLine("Press any key to close the application");
  37. Console.ReadKey();
  38. }
  39. // simple log provider to get something to the console
  40. private class ConsoleLogProvider : ILogProvider
  41. {
  42. public Logger GetLogger(string name)
  43. {
  44. return (level, func, exception, parameters) =>
  45. {
  46. if (level >= LogLevel.Info && func != null)
  47. {
  48. Console.WriteLine("[" + DateTime.Now.ToLongTimeString() + "] [" + level + "] " + func(), parameters);
  49. }
  50. return true;
  51. };
  52. }
  53. public IDisposable OpenNestedContext(string message)
  54. {
  55. throw new NotImplementedException();
  56. }
  57. public IDisposable OpenMappedContext(string key, object value, bool destructure = false)
  58. {
  59. throw new NotImplementedException();
  60. }
  61. }
  62. }
  63. public class HelloJob : IJob
  64. {
  65. public async Task Execute(IJobExecutionContext context)
  66. {
  67. await Console.Out.WriteLineAsync("Greetings from HelloJob!");
  68. }
  69. }
  70. }

现在去探索 Quartz.NET 吧! 您可以继续阅读教程