Quartz.NET 是一個開源的任務管理系統,能夠延遲執行、定期執行任務,簡單來說,如果你需要建立排程來執行任務,Quartz.net 非常適合你!
Quartz.NET 的核心包含: Scheduler、Job、Trigger、JobDetail、JobDataMap 官方說明解釋的不夠詳細,筆者花了很多時間才搞懂。 在使用前,至少需要先了解以下幾點,
安裝 Quartz 和 Quartz.Extensions.Hosting
建立一個 HelloWorldJob ,執行時會顯示當前時間:
在上方 17-21 行的地方建立了觸發器,每秒鐘就會觸發一次,一直執行。
RepeatForever 是一直重複執行,也可以替換為 WithRepeatCount(10) 執行 10 次,或自訂次數
WithIntervalInSeconds 是使用秒來設定週期,也可以使用 WithIntervalInMinutes 指定分鐘、WithIntervalInHours 指定小時等,或是不使用 WithSimpleSchedule ,直接使用 WithCronSchedule 透過 cron 指定週期
註: 不使用 dynamic 或 object 是因為 System.Text.Json 不能很方便的處理 範例輸出:
範例輸出:
TriggerState 內容:
參考資料:
Quartz.NET
Quartz.NET 的核心包含: Scheduler、Job、Trigger、JobDetail、JobDataMap 官方說明解釋的不夠詳細,筆者花了很多時間才搞懂。 在使用前,至少需要先了解以下幾點,
- Job: 定義要執行的工作
- JobDetail: 儲存一個或多個 Job
- Trigger: 定義什麼時候要執行 JobDetail
安裝
在 ASP.NET Core 6 上使用 Quartz,最少只需要安裝 Quartz 即可,不過如果想要使用依賴注入和配合 ASP.NET Core 的生命週期,則還需要安裝 Quartz.Extensions.Hosting ,在本篇示範中請兩個都安裝。安裝 Quartz 和 Quartz.Extensions.Hosting
dotnet add package Quartz
dotnet add package Quartz.Extensions.Hosting
建立 Job
Job 就是任務,在類別中繼承 IJob 介面(interface),實作 Execute 即可,執行 Job 時 Execute 方法就會被呼叫。建立一個 HelloWorldJob ,執行時會顯示當前時間:
public class HelloWorldJob : IJob
{
public Task Execute(IJobExecutionContext context)
{
Console.WriteLine($"Hello World! {DateTime.Now:yyyy-MM-dd HH:mm:ss}");
return Task.CompletedTask;
}
}
宣告任務
builder.Services.AddQuartzHostedService(options => options.WaitForJobsToComplete = true);
builder.Services.AddQuartz(quartz =>
{
quartz.UseMicrosoftDependencyInjectionJobFactory();
// 建立 Job
var jobKey = new JobKey("HelloWord", "HelloWordGroup");
quartz.AddJob<HelloWorldJob>(opts =>
{
opts.WithIdentity(jobKey);
opts.StoreDurably();
});
// 建立觸發器,自動執行 Job
quartz.AddTrigger(opts =>
{
opts.ForJob(jobKey);
opts.WithIdentity("HelloWordTrigger", "HelloWordGroup");
opts.WithSimpleSchedule(x => x.WithIntervalInSeconds(1).RepeatForever());
});
});
在上方 17-21 行的地方建立了觸發器,每秒鐘就會觸發一次,一直執行。
RepeatForever 是一直重複執行,也可以替換為 WithRepeatCount(10) 執行 10 次,或自訂次數
WithIntervalInSeconds 是使用秒來設定週期,也可以使用 WithIntervalInMinutes 指定分鐘、WithIntervalInHours 指定小時等,或是不使用 WithSimpleSchedule ,直接使用 WithCronSchedule 透過 cron 指定週期
.WithCronSchedule("* * * * * ?")); // 每秒重複
查看所有 Job
[Route("api")]
public class JobController : ControllerBase
{
private readonly ISchedulerFactory _schedulerFactory;
public JobController(ISchedulerFactory schedulerFactory)
{
_schedulerFactory = schedulerFactory;
}
/// <summary>
/// 顯示所有 Job
/// </summary>
[HttpGet("jobs")]
public async Task<IActionResult> GetScheduledJobs()
{
var scheduler = await _schedulerFactory.GetScheduler();
List<Dictionary<string, string>> jobs = new();
var allJobKeys = await scheduler.GetJobKeys(GroupMatcher<JobKey>.AnyGroup());
foreach (JobKey? jobKey in allJobKeys)
{
IJobDetail? jobDetail = await scheduler.GetJobDetail(jobKey);
jobs.Add(new Dictionary<string, string>
{
{ "name", jobDetail.Key.Name },
{ "group", jobDetail.Key.Group },
{ "type", jobDetail.JobType.ToString() },
{ "description", jobDetail.Description ?? string.Empty },
});
}
return Ok(jobs);
}
}
註: 不使用 dynamic 或 object 是因為 System.Text.Json 不能很方便的處理 範例輸出:
[
{
"name": "HelloWord",
"group": "HelloWordGroup",
"type": "WebApplicationDynamicQuartz.Jobs.HelloWorldJob",
"description": ""
}
]
查看所有 Trigger
[Route("api")]
public class TriggerController : ControllerBase
{
private readonly ISchedulerFactory _schedulerFactory;
public TriggerController(ISchedulerFactory schedulerFactory)
{
_schedulerFactory = schedulerFactory;
}
[HttpGet("triggers")]
public async Task<IActionResult> GetScheduledTriggers()
{
var scheduler = await _schedulerFactory.GetScheduler();
List<Dictionary<string, string>> triggers = new();
var allTriggerKeys = await scheduler.GetTriggerKeys(GroupMatcher<TriggerKey>.AnyGroup());
foreach (var triggerKey in allTriggerKeys)
{
var trigger = await scheduler.GetTrigger(triggerKey);
TriggerState triggerState = await scheduler.GetTriggerState(trigger.Key);
triggers.Add(new Dictionary<string, string>
{
{ "name", trigger.Key.Name },
{ "group", trigger.Key.Group },
{ "type", trigger.GetType().ToString() },
{ "status", triggerState.ToString() },
{ "description", trigger.Description ?? string.Empty },
{ "startTimeUtc", trigger.StartTimeUtc.ToString() },
{ "endTimeUtc", trigger.EndTimeUtc.ToString() ?? string.Empty },
{ "nextFireTimeUtc", trigger.GetNextFireTimeUtc().ToString() ?? string.Empty },
{ "previousFireTimeUtc", trigger.GetPreviousFireTimeUtc().ToString() ?? string.Empty },
});
}
return Ok(triggers);
}
}
範例輸出:
[
{
"name": "HelloWordTrigger",
"group": "HelloWordGroup",
"type": "Quartz.Impl.Triggers.SimpleTriggerImpl",
"status": "Normal",
"description": "",
"startTimeUtc": "2023/3/19 下午 03:27:55 +00:00",
"endTimeUtc": "",
"nextFireTimeUtc": "2023/3/19 下午 03:28:00 +00:00",
"previousFireTimeUtc": "2023/3/19 下午 03:27:55 +00:00"
}
]
TriggerState 內容:
- Normal: 普通,等待下次執行
- Paused: 暫停
- Complete: 執行完畢
- Error: 錯誤,不會再執行
- Blocked: 鎖定
- None
依照 Job 建立新的 Trigger
string jobName = "HelloWord";
string jobGroup = "HelloWordGroup";
string cron = "* * * * * ?"; // 每秒執行
var scheduler = await _schedulerFactory.GetScheduler();
var jobKey = new JobKey(jobName, jobGroup);
var jobExists = await scheduler.CheckExists(jobKey);
if (!jobExists)
{
Console.WriteLine($"Job {jobName} does not exist");
return;
}
var triggerKey = new TriggerKey($"{jobName}Trigger", jobGroup);
var triggerExists = await scheduler.CheckExists(triggerKey);
if (triggerExists)
{
Console.WriteLine($"Trigger {triggerKey} already exists");
return;
}
var trigger = TriggerBuilder.Create()
.WithIdentity(triggerKey)
.StartNow()
.WithCronSchedule(cron)
.ForJob(jobKey)
.Build();
await scheduler.ScheduleJob(trigger);
依照 Job 移除 Trigger
string jobName = "HelloWord";
string jobGroup = "HelloWordGroup";
var scheduler = await _schedulerFactory.GetScheduler();
var jobKey = new JobKey(jobName, jobGroup);
var jobExists = await scheduler.CheckExists(jobKey);
if (!jobExists)
{
Console.WriteLine($"Job {jobName} does not exist");
return;
}
var triggerKey = new TriggerKey($"{jobName}Trigger", jobGroup);
var triggerExists = await scheduler.CheckExists(triggerKey);
if (!triggerExists)
{
Console.WriteLine($"Trigger {triggerKey} does not exist");
return;
}
await scheduler.UnscheduleJob(triggerKey);
Console.WriteLine($"Trigger {triggerKey} removed");
參考資料:
Quartz.NET
感謝教學~
回覆刪除