从Quartz.net中正在运行的作业中获取数据

时间:2014-10-10 11:41:09

标签: c# 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()中途从作业中取回数据?

2 个答案:

答案 0 :(得分:8)

在作业完成之前,工作数据更改似乎不可用。因此,请使用专用数据结构来监视作业状态。在下面的示例中,我使用一个简单的公共静态属性StatusInfo公开了状态信息,该属性可以随时用于日志记录作业。

还有一个小改动:我已将WriteAllLines替换为AppendAllLines

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 :(得分: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);
}

你会看到,一旦调用了execute方法,作业就会运行并完成它的工作,并且只在作业执行完毕后返回,因此无法接收更新。这是在JobRunShell.cs文件中,以防您有兴趣查看完整的上下文。

您可以使用作业侦听器通知作业何时开始运行,错误输出或完成。虽然这不会给你提供进展信息。如果您想跟踪工作进度,可以:

  1. 使用类似于日志记录模型的内容,您可以在其中将作业传递给记录器,并将其进度记录到记录器中。然后您的其他工作可以从此记录器中读取以查找状态。您必须使这个记录器全局可用,因为作业在不同的线程上运行,并且需要处理线程注意事项。
  2. 您可以为您更换JobRunShell的实现。内置的JobRunShell具有对作业执行上下文的引用,因此它可以读取数据映射(可能使用计时器?),然后报告状态。您必须为此创建自己的IJobRunShellFactory。这是默认的jobRunShellFactory实现。
  3. Quartz.Net在不同的线程上执行作业,因此在它们正在进行时与它们进行通信时会遇到与在飞行中尝试与任何其他常规线程进行通信时相同的问题。所以,如果你曾试图解决这个问题,那么你就知道自己面临的是什么。