Quartz.NET 基础概念
在 Quartz.NET 中,Job 和 Trigger 是两个核心概念,它们通过调度器(Scheduler)进行管理和执行。为了帮助你理解它们之间的关系,以及任务组和触发器组的关系,我将通过文字描述一个思维导图的结构:
Quartz.NET
├── Scheduler
│ ├── Job
│ │ ├── JobDetail
│ │ │ ├── JobKey (JobName, JobGroup)
│ │ │ └── JobDataMap
│ │ └── IJob (Job Interface)
│ ├── Trigger
│ │ ├── TriggerDetail
│ │ │ ├── TriggerKey (TriggerName, TriggerGroup)
│ │ │ ├── TriggerDataMap
│ │ │ └── Schedule (Cron, Simple, etc.)
│ └── Listener
│ ├── JobListener
│ └── TriggerListener
解释:
- Scheduler(调度器):
- 管理和协调 Job 和 Trigger 的执行。
- 可以注册多个 Job 和 Trigger。
- Job(任务):
- JobDetail:包含 Job 的定义信息。
- JobKey:由 JobName 和 JobGroup 组成,用于唯一标识一个 Job。
- JobDataMap:用于存储 Job 的参数和数据。
- IJob:所有任务必须实现的接口,定义了
Execute
方法。
- Trigger(触发器):
- TriggerDetail:包含 Trigger 的定义信息。
- TriggerKey:由 TriggerName 和 TriggerGroup 组成,用于唯一标识一个 Trigger。
- TriggerDataMap:用于存储 Trigger 的参数和数据。
- Schedule:定义触发器的调度策略,比如 Cron 表达式或简单时间间隔。
- Listener(监听器):
- JobListener 和 TriggerListener:用于监听 Job 和 Trigger 的执行事件。
关系:
- 一个 Job 可以与一个或多个 Trigger 关联。
- JobGroup 和 TriggerGroup 是用于组织和管理 Job 和 Trigger 的逻辑分组。
- Scheduler 负责协调 Job 和 Trigger 的执行,确保在指定的时间和条件下执行 Job。
这种结构允许灵活地定义和管理任务调度逻辑,支持复杂的调度需求。
使用探讨
我有一个webapi 功能是:
定时启用运行多个dll, dll 是随时增加改变的
用户想创建一个任务定时运行dll, 下面哪种方式好?好在哪?
1.创造一个job同时创建触发器,用户数据存在JobDataMap中
2.不创建新的job,只创建Trigger,用户数据存在TriggerDataMap中
在 Quartz.NET 中,选择如何存储用户数据以及如何管理任务和触发器的关系,取决于你的具体需求和应用场景。对于你的用例,即需要定时运行多个 DLL,并且这些 DLL 是动态增加和变化的,下面是对两种方案的分析:
方案1:创建一个Job同时创建触发器,用户数据存在JobDataMap中
优点:
- 清晰的职责分离:Job 负责执行逻辑,Trigger 负责调度。这种分离使得代码结构更清晰。
- 易于管理和扩展:每个 Job 可以有自己的逻辑和数据,这使得管理不同的任务变得更加灵活。
- 单一责任原则:Job 只需关注执行任务的逻辑,而不需要关心何时执行。
适用场景:
- 当每个 DLL 的执行逻辑较为复杂,需要单独的配置和数据。
- 当需要对不同的 Job 进行独立的管理和监控时。
方案2:不创建新的Job,只创建Trigger,用户数据存在TriggerDataMap中
优点:
- 减少Job的数量:如果所有 DLL 的执行逻辑相似或相同,可以通过一个通用的 Job 来执行所有任务。
- 更少的资源占用:减少 Job 的数量可能会减少调度器的管理开销。
适用场景:
- 当所有 DLL 的执行逻辑相同或非常相似,只是数据不同。
- 当系统资源有限,需要尽可能减少 Job 的数量。
结论
如果每个 DLL 的执行逻辑可能不同,或者需要不同的配置和数据,建议使用方案1。这种方式提供了更高的灵活性和可维护性。
如果所有 DLL 的执行逻辑基本相同,并且你希望通过一个通用的机制来处理所有任务,方案2 可能更为合适。这种方式可以减少管理的复杂性和资源占用。
关于多个触发器的探讨
当一个作业有多个触发器,并且每个触发器都使用 .UsingJobData("ExampleKey", "ExampleValue")
来传递不同的数据时,作业执行时使用的是触发当前执行的触发器所提供的数据。
具体行为
- 触发器级别的数据优先:当作业被一个触发器触发时,Quartz.NET 会将该触发器的
JobDataMap
合并到作业的JobDataMap
中。因此,如果触发器和作业都定义了同一个键,触发器的数据将覆盖作业的数据。 - 访问数据:在作业的
Execute
方法中,使用context.Trigger.JobDataMap
可以直接访问触发器级别的数据,而使用context.MergedJobDataMap
可以访问合并后的数据(触发器数据优先)。
示例
假设你有以下两个触发器,它们分别设置了不同的 ExampleKey
值:
// 触发器1
var trigger1 = TriggerBuilder.Create()
.WithIdentity("trigger1", "group1")
.ForJob(jobKey)
.UsingJobData("ExampleKey", "ValueFromTrigger1")
.Build();
// 触发器2
var trigger2 = TriggerBuilder.Create()
.WithIdentity("trigger2", "group1")
.ForJob(jobKey)
.UsingJobData("ExampleKey", "ValueFromTrigger2")
.Build();
在作业中:
public class MyJob : IJob
{
public Task Execute(IJobExecutionContext context)
{
// 获取触发器数据
var exampleValueFromTrigger = context.Trigger.JobDataMap.GetString("ExampleKey");
// 获取合并后的数据(触发器数据优先)
var exampleValueMerged = context.MergedJobDataMap.GetString("ExampleKey");
Console.WriteLine($"Trigger Data: {exampleValueFromTrigger}");
Console.WriteLine($"Merged Data: {exampleValueMerged}");
return Task.CompletedTask;
}
}
当作业被 trigger1
触发时,ExampleKey
的值将是 ValueFromTrigger1
。当被 trigger2
触发时,值将是 ValueFromTrigger2
。因此,作业执行时使用的是当前触发的触发器所提供的数据。