2014-10-10 42 views
4

我正在嘗試在Quartz.net中創建一個作業,該作業將監視所有其他作業的狀態並定期更新日誌文件。一旦該工作完成執行,它只會從工作中獲得數據,但我試圖獲得工作狀態的即時信息。在Quartz.net中從正在運行的作業中獲取數據

我寫了儘可能簡單的測試工作,測試的一半工作(這是令人沮喪的,因爲我不能說實際的代碼中有什麼不同)。這是測試代碼:

的工作

[PersistJobDataAfterExecution] 
[DisallowConcurrentExecution] 
class SimpleFeedbackJob : IInterruptableJob 
{ 
    private DateTime _lastRun; 
    public string LastRun { get { return _lastRun.ToString(); } } 
    private string _status; 

    public void Interrupt() 
    { 
    } 

    public void Execute(IJobExecutionContext context) 
    { 
     _status = "working"; 
     _lastRun = DateTime.Now; 

     JobDataMap jobData = context.JobDetail.JobDataMap; 
     jobData["time"] = LastRun; 
     jobData["status"] = _status; 

     DateTime n = DateTime.Now.AddSeconds(5); 
     while (DateTime.Now < n) { } 
     //Thread.Sleep(5000); 

     _status = "idle"; 
     jobData["status"] = _status; 
    } 
} 

public class LogUpdaterJob : IInterruptableJob 
{ 
    private IScheduler _scheduler = TaskManager.Scheduler; //This is the same scheduler that will call this task :/ 
    private string _filepath = Configs.BasePath + @"Logs\log.txt"; 

    public void Execute(IJobExecutionContext context) 
    { 
     Func<string, string> UpdatedLineData 
      = name => 
      { 
       JobKey jobKey = _scheduler.GetJobKeys(GroupMatcher<JobKey>.GroupContains("test")).Where(k => k.Name == name).First(); 
       IJobDetail job = _scheduler.GetJobDetail(jobKey); 
       ITrigger trigger = _scheduler.GetTriggersOfJob(jobKey).First(); 

       string status = job.JobDataMap.Get("time") as string; 
       string time = job.JobDataMap.Get("status") as string; 

       return string.Format("{0,-25} {1,-25}", time, status); 
      }; 

     List<string> lines = new List<string>(); 
     lines.Add(UpdatedLineData("feedback_test")); 
     File.WriteAllLines(_filepath, lines); 
    } 

    public void Interrupt() 
    { 
    } 
} 

相關提取物TaskScheduler

private static IScheduler _scheduler = StdSchedulerFactory.GetDefaultScheduler(); 
public static IScheduler Scheduler { get { return _scheduler; } } 

public void Run() 
{ 
    _scheduler.Start(); 

    IJobDetail feedbackJob = JobBuilder.Create<SimpleFeedbackJob>() 
             .WithIdentity("feedback_test", "test") 
             .UsingJobData("time", "") 
             .UsingJobData("status", "") 
             .Build(); 

    ITrigger feedbackTrigger = TriggerBuilder.Create() 
              .WithIdentity("feedback_test", "test") 
              .WithSimpleSchedule(x => x.WithIntervalInSeconds(10) 
                     .RepeatForever()) 
              .Build(); 

    IJobDetail loggerJob = JobBuilder.Create<LogUpdaterJob>() 
            .WithIdentity("LogUpdater", "Admin") 
            .Build(); 

    ITrigger loggerTrigger = TriggerBuilder.Create() 
              .WithIdentity("LogUpdater", "Admin") 
              .WithSimpleSchedule(x => x.WithIntervalInSeconds(1) 
                    .RepeatForever()) 
              .Build(); 

    _scheduler.ScheduleJob(feedbackJob, feedbackTrigger); 
    _scheduler.ScheduleJob(loggerJob, loggerTrigger); 
} 

因此這並輸出一些數據log.txt,它得到的最後一次運行時是正確的但它只顯示"idle"的狀態,我認爲這應該是"working"的一半時間。換言之,我希望在作業仍在運行時將作業數據寫入並訪問。

是否有可能通過這樣的作業Execute()中途從作業中取回數據?

+1

我是Quartz.NET的新手,因此無法直接回答,但是......也許您可以用不同的方式解決問題?既然看起來你希望你所有的工作都能引出相同的行爲(例如將狀態/進度報告給一個普通的日誌),那麼它是否可以使你的所有工作都從一個基類繼承?只是一個想法... [希望我可以回答的賞金;-)] – InteXX 2014-10-13 21:57:24

+0

@InteXX這正是我在過渡期間所做的(即使我得到了,現在不太可能再改變它了在這個項目中,時間緊迫的時候,這是一個答案) - 但這意味着每次工作要寫時都會鎖定,而且我不相信我已經以100%線程安全的方式完成了這項工作。這也意味着我必須等待工作才能運行,然後才能從中獲得某些信息。但無論哪種方式,當然必須有辦法從Quartz.net的工作中獲取我的特定問題的數據... – Dan 2014-10-13 22:02:15

+0

迄今爲止,我還沒有遇到過一個項目,它沒有按時間:)確定,祝你好運。如果我想到一些事情,我會讓你知道的。 – InteXX 2014-10-13 22:48:14

回答

8

看起來作業數據的變化在作業完成之前是不可用的。因此改爲使用專用數據結構來監視作業狀態。在下面的示例中,我已經使用簡單的公共靜態屬性StatusInfo公開了狀態信息,該日誌可以隨時用於日誌記錄作業。

還有一個小小的變化:我用AppendAllLines替換了WriteAllLines

class StatusInfo 
{ 
    public DateTime LastRun; 
    public string Status; 
} 

[PersistJobDataAfterExecution] 
[DisallowConcurrentExecution] 
class SimpleFeedbackJob : IInterruptableJob 
{ 
    public static StatusInfo StatusInfo; 

    static SimpleFeedbackJob() 
    { 
     SetStatus("idle"); 
    } 

    public void Interrupt() 
    { 
    } 

    public void Execute(IJobExecutionContext context) 
    { 
     SetStatus("working"); 

     Thread.Sleep(5000); 

     SetStatus("idle"); 
    } 

    private static void SetStatus(string status) 
    { 
     StatusInfo = new StatusInfo 
      { 
       LastRun = DateTime.Now, 
       Status = status 
      }; 
    } 
} 

class LogUpdaterJob : IInterruptableJob 
{ 
    private string _filepath = @"D:\Temp\Logs\log.txt"; 

    public void Execute(IJobExecutionContext context) 
    { 
     List<string> lines = new List<string>(); 
     var statusInfo = SimpleFeedbackJob.StatusInfo; 
     lines.Add(String.Format("{0,-25} {1,-25}", 
      statusInfo.LastRun, 
      statusInfo.Status)); 
     File.AppendAllLines(_filepath, lines); 
    } 

    public void Interrupt() 
    { 
    } 
} 
+1

這看起來像是有效的。在這種情況下,JobDataMap究竟有什麼意義? – Dan 2014-10-16 16:35:04

+1

它的主要目的是允許使用不同的參數集來調度同一類型作業的多個實例。如果你想堅持數據庫中的工作,它更加方便。例如,如果你的進程沒有連續運行,或者你正在運行Quartz集羣。在這種情況下,Quartz將負責序列化和數據庫處理。 – 2014-10-16 20:25:40

+0

等待......如果我有許多從SimpleFeedbackJob繼承的作業,這仍然可以工作嗎?有沒有辦法讓子類上的'StatusInfo'靜態? – Dan 2014-10-17 15:00:51

1

簡短的回答是,沒有辦法做到這一點開箱即用。以下是在Quartz.Net中執行您的工作的代碼。

// Execute the job 
try 
{ 
    if (log.IsDebugEnabled) 
    { 
     log.Debug("Calling Execute on job " + jobDetail.Key); 
    } 
    job.Execute(jec); 
    endTime = SystemTime.UtcNow(); 
} 
catch (JobExecutionException jee) 
{ 
    endTime = SystemTime.UtcNow(); 
    jobExEx = jee; 
    log.Info(string.Format(CultureInfo.InvariantCulture, "Job {0} threw a JobExecutionException: ", jobDetail.Key), jobExEx); 
} 
catch (Exception e) 
{ 
    endTime = SystemTime.UtcNow(); 
    log.Error(string.Format(CultureInfo.InvariantCulture, "Job {0} threw an unhandled Exception: ", jobDetail.Key), e); 
    SchedulerException se = new SchedulerException("Job threw an unhandled exception.", e); 
    qs.NotifySchedulerListenersError(
    string.Format(CultureInfo.InvariantCulture, "Job ({0} threw an exception.", jec.JobDetail.Key), se); 
    jobExEx = new JobExecutionException(se, false); 
} 

你會看到,一旦執行方法被調用,運行作業關閉並做它的事情,只有當工作執行完畢返回,所以沒有辦法接收更新。這是在JobRunShell.cs文件中,以防您有興趣查看完整的上下文。

您可以使用作業偵聽器通知作業何時開始運行,出錯或完成。儘管如此,這不會給你提供進展信息。如果你想跟蹤你的工作你可以的進步:

  1. 使用類似的日誌記錄模式,這裏通過作業到記錄器的引用,並記錄其進步吧。然後你的其他工作可以從這個記錄器讀取狀態。你必須使這個記錄器全局可用,因爲這些作業是在不同的線程上運行的,並且需要考慮線程的考慮因素。
  2. 你可以換出你的JobRunShell的實現。內置的JobRunShell具有對作業執行上下文的引用,因此它能夠讀取數據映射(可能使用定時器?),然後報告狀態。你必須爲此創建你自己的IJobRunShellFactory。這是默認的jobRunShellFactory實現。

Quartz.Net在不同的線程上執行作業,因此當它們正在進行時與它們進行通信時會遇到同樣的問題,因爲它在飛行中嘗試與任何其他常規線程進行通信。所以,如果你曾經試圖解決這個問題,那麼你知道你在對抗什麼。

相關問題