Quartz 作业存储(Job Stores)

作业存储(JobStores)负责跟踪您提供给调度器的所有“工作数据”:作业、触发器、日历等。为您的 Quartz 调度器实例选择合适的 IJobStore 实现是重要步骤。幸运的是,一旦了解了它们之间的差异,这个选择应该非常简单。您需要在提供给 SchedulerFactory 的属性文件(或对象)中声明调度器应使用的 JobStore(及其配置设置),以便生成调度器实例。

警告
绝不要在代码中直接使用 JobStore 实例。出于某种原因,许多人试图这样做。JobStore 是供 Quartz 本身后台使用的。您必须通过配置告诉 Quartz 使用哪个 JobStore,但在代码中您应该仅与 Scheduler 接口交互。

RAMJobStore

RAMJobStore 是最简单的作业存储方式,也是 CPU 时间性能最优的。顾名思义,RAMJobStore 将其所有数据保存在 RAM 中。这就是它为何能快速响应,同时也是配置简便的原因。缺点是当您的应用程序结束(或崩溃)时,所有的调度信息都会丢失——这意味着 RAMJobStore 不能保持作业和触发器的“非易失性”设置。对于某些应用来说,这是可接受的,甚至是期望的行为,但对于其他应用而言,这可能是灾难性的。

配置 Quartz 使用 RAMJobStore

// 实际上这是默认值,因此您无需显式设置
quartz.jobStore.type = Quartz.Simpl.RAMJobStore, Quartz

若要使用 RAMJobStore(假设您使用的是 StdSchedulerFactory),则无需执行特殊操作。Quartz.NET 的默认配置即使用 RAMJobStore 作为作业存储实现。

ADO.NET 作业存储(AdoJobStore)

AdoJobStore 的名字恰如其分——它通过 ADO.NET 将所有数据保存在数据库中。因此,它的配置比 RAMJobStore 稍微复杂一些,速度也略慢。然而,性能上的劣势并不是特别糟糕,特别是如果您为数据库表构建了主键索引的话。

使用 AdoJobStore

要使用 AdoJobStore,您首先需要为 Quartz.NET 创建一组数据库表。您可以在 Quartz.NET 发行版的 "database/tables" 目录中找到表创建 SQL 脚本。如果没有针对您数据库类型的脚本,只需查看现有的一个并根据您的数据库需求进行必要的修改。需要注意的是,在这些脚本中,所有表都以前缀 QRTZ_ 开始(例如表 QRTZ_TRIGGERSQRTZ_JOB_DETAIL)。这个前缀实际上可以根据您的喜好设置,只要您告知 AdoJobStore 该前缀是什么(在您的 Quartz.NET 属性中)。使用不同的前缀对于在同一数据库中为多个调度器实例创建多套表可能很有用。

当前,作业存储的内部实现唯一选项是 JobStoreTX,它自己创建事务。这与 Java 版本的 Quartz 不同,Java 版本中还有使用 J2EE 容器管理事务的 JobStoreCMT 选项。

配置 Quartz 使用 JobStoreTX

quartz.jobStore.type = Quartz.Impl.AdoJobStore.JobStoreTX, Quartz

接下来,您需要为 JobStore 选择一个 IDriverDelegate 实现。DriverDelegate 负责完成针对特定数据库所需的任何 ADO.NET 工作。StdAdoDelegate 是一个使用 “原生” ADO.NET 代码(和 SQL 语句)来完成工作的委托。如果没有为您的数据库专门制作另一个委托,请尝试使用此委托——特定的委托通常具有更好的性能或针对数据库特定问题的解决方案。其他委托可以在 Quartz.Impl.AdoJobStore 命名空间或其子命名空间中找到。

提示
如果您使用默认的 StdAdoDelegate,Quartz.NET 会发出警告,因为当您需要从大量触发器中选择时,其性能较差。特定的委托有特殊的 SQL 来限制结果集长度(如 SqlServerDelegate 使用 TOP n,PostgreSQLDelegate 使用 LIMIT n,OracleDelegate 使用 ROWCOUNT() <= n 等)。

一旦选择了委托,将其类名设置为 AdoJobStore 使用的委托。

配置 AdoJobStore 使用 DriverDelegate

quartz.jobStore.driverDelegateType = Quartz.Impl.AdoJobStore.StdAdoDelegate, Quartz

接下来,您需要通知 JobStore 您正在使用的表前缀(如上所述)。

配置 AdoJobStore 使用表前缀

quartz.jobStore.tablePrefix = QRTZ_

最后,您需要设置 JobStore 应使用的数据源名称。所命名的数据源也必须在您的 Quartz 属性中定义。在此例中,我们指定 Quartz 应使用配置属性其他部分定义的名为 "myDS" 的数据源。

配置 AdoJobStore 使用数据源名称

quartz.jobStore.dataSource = myDS

配置的最后一环是设置数据源的连接字符串信息和数据库提供商。连接字符串是特定于驱动程序的标准 ADO.NET 连接。数据库提供商是对数据库驱动程序的抽象,以在数据库驱动程序和 Quartz 之间创建松散耦合。

设置数据源的连接字符串和数据库提供商

quartz.dataSource.myDS.connectionString = Server=localhost;Database=quartz;Uid=quartznet;Pwd=quartznet
quartz.dataSource.myDS.provider = MySql

当前支持的数据库提供商包括:

  • SqlServer - SQL Server 驱动
    • 对于完整框架,默认为 System.Data.SqlClient(Quartz 3.1 除外)
    • 从 Quartz 3.2 开始,对于.NET Core,默认为 Microsoft.Data.SqlClient
  • SystemDataSqlClient - 在.NET Core 上单独可用(完整框架的默认)
  • MicrosoftDataSqlClient - 在完整框架上单独可用(.NET Core 的默认)
  • OracleODP - Oracle 的 Oracle 驱动
  • OracleODPManaged - Oracle 用于 Oracle 11 的托管驱动
  • MySql - MySQL Connector/.NET
  • SQLite - SQLite ADO.NET 提供程序
  • SQLite-Microsoft - Microsoft SQLite ADO.NET 提供程序
  • Firebird - Firebird ADO.NET 提供程序
  • Npgsql - PostgreSQL Npgsql
提示
有许多社区贡献的提供程序,比如针对 NoSQL 数据库的。
但它们并未得到 Quartz.NET 项目的官方支持。

如果可用,您可以也应该使用驱动程序的最新版本,只需创建程序集绑定重定向即可

如果您的调度器非常繁忙(即几乎始终执行与线程池大小相同数量的作业),那么您应该将数据源中的连接数设置为大约线程池大小加 1。这通常在 ADO.NET 连接字符串中配置 - 请参阅您的驱动程序实现了解详情。

quartz.jobStore.useProperties 配置参数可以设置为 "true"(默认为 false),以指示 AdoJobStore 所有 JobDataMaps 中的值都将为字符串,因此可以作为名称-值对存储,而不是以序列化形式存储更复杂的对象在 BLOB 列中。从长远来看,这更安全,因为您避免了非字符串类版本化的问题,这些问题在将非字符串类序列化到 BLOB 中时会出现。

配置 AdoJobStore 使用字符串作为 JobDataMap 值

提示
这是推荐的配置,因为它大大降低了类型序列化问题的可能性。
quartz.jobStore.useProperties = true

选择序列化器

Quartz.NET 支持二进制和 JSON 序列化。JSON 序列化来自单独的 Quartz.Serialization.Json NuGet 包。

提示
对于新建项目,推荐使用 JSON 作为数据库中存储数据的持久格式。您还应强烈考虑设置 useProperties 为 true 以限制键值为字符串。
// "json" 是 "Quartz.Simpl.JsonObjectSerializer, Quartz.Serialization.Json" 的别名
quartz.serializer.type = json
在本文档中