天天看點

Quartz.NET--JOB作業

作業流程是在排程器的統一排程下完成的,它可以排程多個作業,觸發器提供作業執行的條件(每天 8:00 am),觸發器與作業關聯,它們是 1:N 的關系,1個觸發器可以關聯1個或多個作業。

附帶的作業

      我們知道要實作自己的作業功能隻要繼承 IJob 接口并實作 Execute(JobExecutionContext context) 方法,再把它添加到排程器,排程器會調用執行Execute(JobExecutionContext context) 方法。調用期間,排程器會跟蹤作業和它們的執行次數。Quartz.NET 預設提供了 FileScanJob 監視某個檔案是否被修改,NativeJob 執行指定程式,NoOpJob 空操作用來給系統調用 ITriggerListener 、IJobListener ,以及 SendMailJob 郵件發送作業

Quartz.NET--JOB作業

它有一個存儲作業執行時資料的重要屬性:MergedJobDataMap,它是 JobDataMap 類型,也可以使用 JobExecutionContext.JobDetail.JobDataMap 來獲得 JobDataMap 對象的引用。作為傳遞參數的容器,JobDataMap 間接繼承了 DirtyFlagMap,DirtyFlagMap 内嵌了 Hashtable 容器,它有一組資料讀寫方法。可以使用 Visual Studio 的類圖來檢視。

Quartz.NET--JOB作業

當實作一個真實的作業類時,一些屬性是需要告知Quartz的,也是您希望該作業需要有的。這就需要通過JobDetail類,這個類在前面我們已經簡單介紹過。

下面我們看一下作業的本質,以及生命周期。還是看看前面的例子:

Lesson1

// construct a scheduler factory 
ISchedulerFactory schedFact = new StdSchedulerFactory(); 
// get a scheduler 
IScheduler sched = schedFact.GetScheduler(); 
sched.Start(); 
// construct job info 
JobDetail jobDetail = new JobDetail("myJob", null, typeof(DumbJob)); 
// fire every hour 
Trigger trigger = TriggerUtils.MakeHourlyTrigger(); 
// start on the next even hour 
trigger.StartTime = TriggerUtils.GetEvenHourDate(DateTime.UtcNow); 
trigger.Name = "myTrigger"; 
sched.ScheduleJob(jobDetail, trigger);      
public class DumbJob : IJob
 {
public DumbJob() { }
publicvoid Execute(JobExecutionContext context)
{
  Console.WriteLine("DumbJob is executing.");
}
 }      

如上例所示,向scheduler提供JobDetail執行個體,就能指向作業執行。scheduler每次執行(Execute方法)作業前,都新生成一個作業執行個體。由于是沒有參數的構造器,每次作業執行,資料都沒清一次。

JobDataMap

 在作業執行個體執行的時候,JobDataMap可以用來儲存任何數量對象(可序列化)的。JobDataMap繼承自IDictionary接口,添加一些便捷的方法,以便存儲和獲得原型資料。

以下是添加到scheduler之前,向JobDataMap存放資料的代碼片段:

jobDetail.JobDataMap[
"jobSays"
] = 
"Hello World!"
;

jobDetail.JobDataMap[
"myFloatValue"
] = 3.141f;

jobDetail.JobDataMap[
"myStateData"
] = 
new
 ArrayList();      

以下是在作業執行時,取資料的代碼片段:

public
 
class
 DumbJob : IJob

{

 
public
 
void
 Execute(JobExecutionContext context)

 
{

 
 
 
string
 instName = context.JobDetail.Name;

 
 
 
string
 instGroup = context.JobDetail.Group;

 
 
 JobDataMap dataMap = context.JobDetail.JobDataMap;

 
 
 
string
 jobSays = dataMap.GetString(
"jobSays"
);


 
 
 
float
 myFloatValue = dataMap.GetFloat(
"myFloatValue"
);

 
 
 ArrayList state = (ArrayList) dataMap[
"myStateData"
];

 
 
 state.Add(DateTime.UtcNow);

 
 
 Console.WriteLine(
"Instance {0} of DumbJob says: {1}"
, instName, jobSays);

 
}

}      

 如果需要用JobStore來做持久化的話,就需要小心存在JobDataMap的是什麼了。因為存放的對象是要被序列化的,那麼就很容易出現對象的版本問題。顯然,.Net的标準資料類型是安全的,除此之外,序列化的對象執行個體,任何變化,都會導緻版本相容問題。或者幹脆隻允許原型資料類型和字元串能儲存,這樣能避免以後序列化版本沖突問題。

在作業執行時,JobDataMap可以從JobExecutionContext中獲得。這時候它是JobDetail和Trigger中的JobDataMap合并,是後來同名對象的覆寫。

下面是在作業執行時,從JobExecutionContext中獲得JobDataMap的代碼片段:

public class DumbJob : IJob
{
    public void Execute(JobExecutionContext context)
    {
        string instName = context.JobDetail.Name;
        string instGroup = context.JobDetail.Group;

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

        string jobSays = dataMap.GetString("jobSays");
        float myFloatValue = dataMap.GetFloat("myFloatValue");
        ArrayList state = (ArrayList) dataMap.Get("myStateData");
        state.Add(DateTime.UtcNow);

        Console.WriteLine("Instance {0} of DumbJob says: {1}", instName, jobSays);
    }
}      

有狀态與無狀态的作業

   以上我們的作業執行個體都是從 IJob 繼承,Quartz.NET 裡還有 IStatefulJob 、IInterruptableJob,它的聲明方式為:

public interface IStatefulJob : IJob 
     { 
     }

public interface IInterruptableJob : IJob 
     { 
         void Interrupt(); 
     }      

      IInterruptableJob 接口提供了一個中斷方法,但是 IStatefulJob 沒有自己的方法。從 Quartz.NET 官方了解到:

一個 Job 執行個體可以被定義為“有狀态的”或者“無狀态的”。在執行無狀态的任務過程中任何對 JobDataMap 所作的更改都将丢失。有狀态的任務恰好相反,它在任務的每次執行之後重新存儲 JobDataMap 。有狀态任務的一個缺點就是它不能并發執行。也就是說,如果任務有狀态,那麼當觸發器試圖觸發它,觸發器就會被阻塞直到前面的執行完成。想使任務有狀态,它就要實作 IStatefulJob 接口而不是實作IJob接口。

作業執行個體,可以被指定為“statefull”,或者“non-statefull”。Non-statefull作業隻在JobDataMap添加到scheduler時有資料。就是意味着,在下一次作業執行時,任何作業狀态資料(JobDataMap)的修改丢将丢失或看不見。正如您猜測的那樣,statefull作業正好相反,作業的每次執行,JobDataMap都會重新儲存,這給statefull作業的負作用,就是不能并發執行。也就是說,如果一個statefull作業,觸發器已經觸發正在執行了,下個觸發器将會被阻塞,直到前一個執行完成。

  辨別一個statefull作業,隻要繼承IStatefullJob接口,而不是IJob接口。

Job 'Instances'

 這個需要最後明确的是,您可以建立一個作業類,通過建立多個JobDetail執行個體,存儲在scheduler中,每個執行個體都有自己的屬性集及JobDataMap。

 當與作業關聯的觸發器觸發時,作業通過JobFactory執行個體化。預設的JobFactory調用Activator.CreateInstance。

Other Attributes Of Jobs

這裡列一下JobDetail對象的其他屬性:

  • Durable - 一個non-durable作業, 當沒有與之關聯的active trigger時,會自動從scheduler中删除的。
  • Volatile - 一個volatile作業, 在re-starts scheduler之間是不保留的。
  • RequestsRecovery - 一個"requests recovery"作業,在scheduler“hard shutdown”的情況下,或者機器斷電的時候,當scheduler重新開機時,作業重新執行。JobExecutionContext.isRecovering()傳回true。
  • JobListeners - 作業可以沒有或多個JobListeners。作業執行時,listeners會被提醒。更多讨論後面專門讨論。

The Job.Execute(..) Method

為什麼作業和觸發分開

很多作業排程程式,作業和觸發沒有明顯的分開界限。有些作業定義為一個作業識别号和相應的執行時間。還有些更像Quartz.NET作業和觸發的聚合。當開發Quartz的時候,Quartz team決定,把排程和所要執行的動作分開(trigger)。據我們所知,這将帶來很多便捷。例如:

  • 作業可以獨立trigger存儲在scheduler中,多個trigger關聯同一個作業。
  • 松耦合可以重新排程已經過期作業,而不需要重新定義。也可以修改替換觸發器而不需要重新定義與之相關的作業。

識别符Identifiers