Quartz.NET 2.x 文件翻譯 - Lesson 3:Jobs 和 Triggers深入瞭解

龐順龍發表於2019-05-11

Quartz.NET 2.x 文件翻譯 - Lesson 3:Jobs 和 Triggers深入瞭解 檢視官網英文原文

返回目錄

正如我們在第2章說到的,我們可以很容易的執行一個job任務,那麼接下來需要我們瞭解知道的job深入內容主要有IJob介面的Execute()方法和JobDetails。

對於一個job任務來說,我們知道如何制定特定type型別和如何編寫實現一個job的程式碼,對於使用Quartz.NET來說我們需要了解它各種各樣的屬性引數,那麼肯定希望有這方面的demo例子了,正如我們上一章說過的,可以通過job的JobDetail明細類來進行分析。

JobDetail明細類的例項是通過JobBuilder建立的,JobBuilder允許我們使用連續的介面方式進行實現和編碼。

我們還是用quartz.net的生命週期程式碼來分析,正如上幾章節提到的程式碼:

Using Quartz.NET

// define the job and tie it to our HelloJob class
IJobDetail job = JobBuilder.Create<HelloJob>()
  .WithIdentity("myJob", "group1")
  .Build();

// Trigger the job to run now, and then every 40 seconds
ITrigger trigger = TriggerBuilder.Create()
  .WithIdentity("myTrigger", "group1")
  .StartNow()
  .WithSimpleSchedule(x => x
    .WithIntervalInSeconds(40)
    .RepeatForever())
  .Build();
  
sched.ScheduleJob(job, trigger);

接下來定義一個job實現類HelloJob:

public class HelloJob : IJob
{
  public void Execute(IJobExecutionContext context)
  {
    Console.WriteLine("HelloJob is executing.");
  }
}

注意這裡,我們提供給scheduler排程的是一個IJobDetail介面,通過引用關係,job的實現類引用來執行任務的。每次scheduler排程執行job任務的時候,排程會在執行Execute方法前建立一個新的實現類,然後繼續執行方法,這就要求我們每一個job任務實現類都需要一個自身建構函式,另外就是在job任務例項類中定義資料儲存欄位屬性等操作是無意義的,因為job執行過程中並不會進行儲存。

說到這裡,也許你會想問“那我怎麼才能給一個job任務例項化類設定一些屬性或者配置呢?”或者“我如何才能跟蹤記錄job任務狀態在執行間隔?”,答案就是:JobDataMap中的key值,也就是JobDetail物件的包含屬性內容中。

JobDataMap

JobDataMap可以儲存容納任意數量的可序列化物件,以便在job例項化執行的時候使用。JobDataMap是IDictionary介面的實現,並新增了一些方便儲存和檢索原始型別資料的方法。

下面演示一小段程式碼來說明將job任務新增到scheduler排程前先將資料新增到JobDataMap:

Setting Values in a JobDataMap

// define the job and tie it to our DumbJob class
IJobDetail job = JobBuilder.Create<DumbJob>()
  .WithIdentity("myJob", "group1") // name "myJob", group "group1"
  .UsingJobData("jobSays", "Hello World!")
  .UsingJobData("myFloatValue", 3.141f)
  .Build();

下面演示下job任務執行時候從JobDataMap中取出資料的程式碼片段:

Getting Values from a JobDataMap

public class DumbJob : IJob
{
  public void Execute(JobExecutionContext context)
  {
    JobKey key = context.JobDetail.Key;

    JobDataMap dataMap = context.JobDetail.JobDataMap;

    string jobSays = dataMap.GetString("jobSays");
    float myFloatValue = dataMap.GetFloat("myFloatValue");

    Console.Error.WriteLine("Instance " + key + " of DumbJob says: " + jobSays + ", and val is: " + myFloatValue);
  }
} 

如果想保持持久化的資料,我們需要注意哪些放置到JobDataMap中,因為物件會被序列化,這很容易帶來版本問題。很明顯,標準的.net型別理論是安全的,但除此之外,已經序列化的類中任意一個屬性的變化,都有可能導致物件資訊的不完整和不準確。

當然,我們也可以將adojobstore和JobDataMap組合成一個model模型,只能儲存string和原始資料庫型別,以此來消除可能出現的序列化問題。

如果我們新增設定了和JobDataMap中的key的name值相同的屬性到job任務class類中,然後quartz的JobFactory會預設自動呼叫這些設定屬性在job任務類例項化的時候,因此,我們要明確的從執行方法中取出我們需要的數值。

Trigger觸發器也有JobDataMaps,在我們需要定時排程執行一個重複性的job任務的時候,就用到Trigger觸發器的JobDataMaps了,對於獨立的Trigger觸發器來說我們就需要提供不同的data資料給job任務。

JobDataMap是在job任務執行過程中由JobExecutionContext當做載體儲存的,這其實是Trigger觸發器和jobdetail任務詳細的一個合併而成的JobDataMap,後者根據相同的name屬性重寫了對應的value值。

下面演示資料從JobExecutionContext在job執行過程中合併JobDataMap的過程:

public class DumbJob : IJob
{
  public void Execute(IJobExecutionContext context)
  {
    JobKey key = context.JobDetail.Key;

    JobDataMap dataMap = context.MergedJobDataMap;  // Note the difference from the previous example

    string jobSays = dataMap.GetString("jobSays");
    float myFloatValue = dataMap.GetFloat("myFloatValue");
    IList<DateTimeOffset> state = (IList<DateTimeOffset>) dataMap["myStateData"];
    state.Add(DateTimeOffset.UtcNow);

    Console.Error.WriteLine("Instance " + key + " of DumbJob says: " + jobSays + ", and val is: " + myFloatValue);
  }
}

或者我們也可以使用JobFactory的依賴注入方式,將資料對映到class中,如下程式碼:

public class DumbJob : IJob
{
    public string JobSays { private get; set; }
    public float FloatValue { private get; set; }
      
  public void Execute(IJobExecutionContext context)
  {
    JobKey key = context.JobDetail.Key;

    JobDataMap dataMap = context.MergedJobDataMap;  // Note the difference from the previous example

    IList<DateTimeOffset> state = (IList<DateTimeOffset>) dataMap["myStateData"];
    state.Add(DateTimeOffset.UtcNow);

    Console.Error.WriteLine("Instance " + key + " of DumbJob says: " + JobSays + ", and val is: " + FloatValue);
    }
}
我們發現整體程式碼量增加了,但是我們的執行方法execute()變得簡潔了。還有人認為程式碼量增加了,但是編碼更加容易了,如果程式碼都是我們們平時的IDE生成的屬性等,而不用我們手寫程式碼取出JobDataMap的數值,那麼這樣的程式碼才算是整潔有效。

Job “Instances”

很多人可能會有疑惑到底是什麼構成了一個個的job任務例項,接下來我們就儘量清晰的講解清楚job任務的狀態和併發等情況。

我們可以建立一個簡單的job任務類,然後建立幾個例項JobDetails,每個例項都有自己設定的屬性和JobDataMap,然後把這些例項新增到scheduler排程。

比如,我們可以建立一個實現了IJob介面的SalesReportJob類,然後新增程式碼,實現通過傳入的引數,如銷售人員名稱實現將對應銷售人員的銷售報表傳送給他,我們也可以建立很多的JobDetails,比如salesreportforjoe或者salesreportformike來區分joe和mike,然後根據JobDataMaps的引數來區分那個job任務進行執行處理。

當一個Trigger觸發器被觸發後,JobDetail會被載入,job任務類在通過JobFactory配置實現排程程式。預設JobFactory只呼叫使用job工作類的預設建構函式。CreateInstance方法會嘗試在JobDataMap中呼叫相匹配的key的names,也許你想建立自己的JobFactory實現,來實現類似於IOC、DI容器等job任務的功能。

對於“Quartz”來說,我們把指定的每個儲存JobDetail明細當做是一個job任務類定義或者是JobDetail明細類定義,或是我們執行的每個job任務類定義、job實現定義。

通常我們僅僅使用job任務這個詞來表明一個job任務類定義或是一個JobDetail的明細而已。當我們需要實現job介面的時候,我們通常使用“job type”這個詞語。

Job State and Concurrency

下面,有一些關於job任務工作狀態資料的一些附加說明(又名JobDataMap)和併發的資訊,有一對屬性可以新增到我們的job任務類中,這一對屬性可以影響Quartz的執行併發等方面。

DisallowConcurrentExecution 是其中一個屬性,可以新增到job任務類,來通知quartz不同時執行我們給定的一個job任務的多個例項。這裡使用的時候一定要注意。在我們前面說過的章節裡面,如果SalesReportJob這個job任務類設定了這個屬性,那麼只能是SalesReportForJoe的一個例項能執行在任務執行時間點,但是我們也可以在執行一個SalesReportForJoe例項的同時執行SalesReportForMike的一個例項。這個約束基於JobDetail的例項而不是job任務類,然而這個約束在quartz的設計過程中又建立在class類本身,因為class的編碼有各種不同之處。

PersistJobDataAfterExecution 是另外一個屬性,可以實現在Execute()方法執行併成功後通知quartz更新儲存從JobDetail的任務明細JobDataMap資料,這樣再次執行同一個job任務的時候就會使用update更新後的資料而不是原來的舊資料了。和DisallowConcurrentExecution屬性一樣,這個屬性也是應用在job定義中,而不是job的class中,雖然在我們編碼過程中它也是在job的class中去設定的屬性。

如果我們使用PersistJobDataAfterExecution屬性,我們需要考慮清楚一併使用DisallowConcurrentExecution屬性,以此避免多個例項執行job的時候,新舊資料儲存的併發問題。

Other Attributes Of Jobs

還有一些JobDetail其他的屬性:

Durability - 如果一個job工作是非持久的,那麼它會自動從scheduler排程中刪除在沒有任何活動的triggers觸發與它相關聯的時候。換言之,非持久的job任務是有生命週期,這取決於它的觸發器的是否存在。 

RequestsRecovery - 如果一個job“RequestsRecovery”,它是在排程執行過程中被強制關閉的(比如執行崩潰或是機器斷電等),那麼它會從新執行在scheduler排程重新啟動後。在這種情況下JobExecutionContext.Recovering屬性將會返回true。

JobExecutionException

最後,簡單說下IJob.Execute(..)方法,該方法執行中的唯一的異常型別是JobExecutionException,因此,在我們編碼的時候,需要用try catch將整個方法包含在內,有空的話多看看和JobExecutionException相關的文件,以便在我們使用的時候能更好的執行各種命令並處理各種異常。

返回目錄

龐順龍最後編輯於:3年前

內容均為作者獨立觀點,不代表八零IT人立場,如涉及侵權,請及時告知。

相關文章