JobBuilder
Create
通過 JobBuilder 的 Create 方法,指定一個作業(yè)任務(wù)(一個實現(xiàn)了 IJob 接口的 Job 類)。IJob 接口只有一個 Execute 方法,我們需要在 Execute 方法中完善我們要執(zhí)行的業(yè)務(wù)代碼。
IJobDetail job = JobBuilder.Create<HelloJob>()
.Build();
public class HelloJob : IJob
{
public async Task Execute(IJobExecutionContext context)
{
await Console.Out.WriteLineAsync("Hi,everybody");
}
}
Execute 方法中的 context 參數(shù)包含作業(yè)執(zhí)行的所有上下文信息,我們可以通過 context 中的一些屬性獲取需要的數(shù)據(jù)。如:
context.Scheduler 調(diào)度器信息
context.Trigger 觸發(fā)器信息
context.JobDetail 作業(yè)信息
context.ScheduledFireTimeUtc 當前觸發(fā)時間
context.NextFireTimeUtc 下一次將被觸發(fā)時間
......
Identity
Job 注冊到 Quartz 調(diào)度器的時候需要設(shè)置一個標識的 name,同時可以為 name 設(shè)置一個組,方便我們對 Job 分類,在同一個組內(nèi)的 name 必須是唯一的。如果沒有指定,name會自動生成,group 為 DEFAULT。
IJobDetail job = JobBuilder.Create<HelloJob>()
.WithIdentity("job1", "jobGroup1");
.Build();
UsingJobData
通過 JobBuilder 的 UsingJobData 方法我們可以為這個 Job 設(shè)置一些附屬信息,當 Job 被執(zhí)行的時候,我們在 Execute 方法中通過 context.JobDetail.JobDataMap 獲取這些信息以供業(yè)務(wù)代碼使用,也可以通過定義同名的屬性,它將會自動注入到屬性值。
var jobDataMap = new JobDataMap();
jobDataMap.Add("name", "beck");
IJobDetail job = JobBuilder.Create<HelloJob>()
.WithIdentity("job1", "jobGroup1");
.UsingJobData(jobDataMap)
.Build();
public class HelloJob : IJob
{
// 映射JobDetail.JobDataMap的name
public string Name { get; set; }
public async Task Execute(IJobExecutionContext context)
{
//var name = context.JobDetail.JobDataMap["name"].ToString();
await Console.Out.WriteLineAsync($"Hi {Name}");
}
}
注意:Job 和 Trigger 中都可以通過 UsingJobData 來設(shè)置附屬信息,在 Execute 方法中,context.MergedJobDataMap 屬性中既包含 Job 的 DataMap,也包含 Trigger 的 DataMap。當存在同名的參數(shù)時,Trigger 會覆蓋 Job 的參數(shù)值。
StoreDurably
默認情況下,當 Job 沒有對應(yīng)的 Trigger 時,Job 是不能直接被加入調(diào)度器中的,或者在 Trigger 過期之后, 沒有關(guān)聯(lián) Trigger 的 Job 也會被刪除。我們可以通過 JobBuilder 的 StoreDurably 使 Job 獨立存儲于調(diào)度器中。
IJobDetail job = JobBuilder.Create<HelloJob>()
.WithIdentity("job1", "jobGroup1")
.UsingJobData(jobDataMap)
.StoreDurably()
.Build();
Job
State
Job 默認是無狀態(tài)的,每次執(zhí)行 Job ,context.JobDetail 獲取到值都是原始數(shù)據(jù),對其的任何修改都不會生效。
如:我們試圖修改 JobDataMap 中的 times 屬性,希望每次執(zhí)行后都加1。
var jobDataMap = new JobDataMap();
jobDataMap.Add("times", 1);
public class HelloJob : IJob
{
public int Times { get; set; }
public async Task Execute(IJobExecutionContext context)
{
context.JobDetail.JobDataMap["times"] = Times + 1;
await Console.Out.WriteLineAsync($"execute {Times} times");
}
}

實際并不會有效,Job 注冊之后,調(diào)度器內(nèi)保存了這個對象,默認情況下是每次通過反射new一個新實例來執(zhí)行的,所以每次修改必然不會有效。但我們可以通過在 Job 類上加上 PersistJobDataAfterExecution Attribute,就可以非常簡單的把 Job 變成有狀態(tài)。
[PersistJobDataAfterExecution]
public class HelloJob : IJob
{
// ...
}

Concurrency
當多個 Trigger 同時觸發(fā)一個 Job 或者 一個 Job 執(zhí)行較長,Trigger 條件已經(jīng)被多次觸發(fā),這種情況下,Job 就出現(xiàn)了并發(fā)問題。在并發(fā)情況下,數(shù)據(jù)的準確性必然會有影響。
我們設(shè)置 Trigger 觸發(fā)周期為1s一次,在 Job 的 Exceute 方法中加入代碼:
Thread.Sleep(2000);

很明顯結(jié)果的錯誤的,但這時我們可以通過在 Job 類上加上 DisallowConcurrentExecution Attribute,就可以完美解決并發(fā)問題。
[DisallowConcurrentExecution]
public class HelloJob : IJob
{
// ...
}
