Quartz Job Scheduler 不触发作业 (C#)
Quartz Job Scheduler Not Firing Jobs (C#)
我在我正在处理的 C# 项目中 运行 遇到了 Quartz(版本 2.2.4)的一个相当令人费解的问题,尽管我尽了最大的努力,但我还是没能解决解决问题。
该项目需要独立安排多个不同的任务;在这种情况下,我专门编写了一个 class 来处理 24 小时周期的作业设置。 class 应该为每个需要安排的任务实例化一次。
下面是有问题的对象:
namespace ProjectFront.Utilities {
public class QuartzScheduler {
public const string QB_TRIGGER = "qbTrigger";
public const string QB_JOB = "qbJob";
public const string QB_GROUP = "qbGroup";
private IScheduler scheduler;
private ITrigger trigger;
private IJobDetail job;
private JobKey jobKey;
public QuartzScheduler(IJob controller, string triggerId, string jobId, string jobGroup, int syncHour, int syncMinute) {
StdSchedulerFactory schedulerFactory = new StdSchedulerFactory();
schedulerFactory.Initialize();
jobKey = new JobKey(jobId, jobGroup);
syncHour = getHour(syncHour);
syncMinute = getMin(syncMinute);
scheduler = schedulerFactory.GetScheduler();
scheduler.Start();
job = JobBuilder.Create(controller.GetType())
.StoreDurably()
.WithIdentity(jobKey)
.Build();
trigger = TriggerBuilder.Create()
.WithIdentity(triggerId)
.ForJob(jobKey)
.StartNow()
.WithSchedule(CronScheduleBuilder.DailyAtHourAndMinute(syncHour, syncMinute))
.Build();
scheduler.ScheduleJob(job, trigger);
scheduler.Start();
}
public void Reschedule(int syncHour, int syncMinute) {
scheduler.Standby();
syncHour = getHour(syncHour);
syncMinute = getMin(syncMinute);
TriggerKey triggerKey = trigger.Key;
trigger = TriggerBuilder.Create()
.WithIdentity(triggerKey)
.ForJob(jobKey)
.StartNow()
.WithSchedule(CronScheduleBuilder.DailyAtHourAndMinute(syncHour, syncMinute))
.Build();
scheduler.RescheduleJob(triggerKey, trigger);
scheduler.Start();
}
public IScheduler GetScheduler() {
return scheduler;
}
public ITrigger GetTrigger() {
return trigger;
}
private int getHour(int num) {
if (num < 4) {
return num + 20;
}
return num - 4;
}
private int getMin(int num) {
while (num >= 60) {
num -= 60;
}
return num;
}
}
}
我已经编写了几个 NUnit 测试来确保对象中的所有内容都能正常工作。它通过了其中两个,但总是让第三个失败;该工作肯定存在,甚至可以重新安排,但在调用 TriggerJob()
时静默失败。重新安排它然后等待它触发同样失败。
这些是测试:
namespace UnitTestProject.BackEnd_UnitTests.Util {
public class ControllerStub : IJob{
public ControllerStub() { }
public void Execute(IJobExecutionContext context)
{
Console.WriteLine("Fired!");
Console.WriteLine(DateTime.Now.ToString());
}
}
[TestFixture]
public class QBSchedulerTest {
public static int fired = 0;
private QuartzScheduler target;
private JobKey expectedJob;
private ControllerStub dummy;
[TestFixtureSetUp]
public void fixture() {
colAlias.LogManager.Adapter = new colAlias.Simple.TraceLoggerFactoryAdapter { Level = colAlias.LogLevel.Info };
expectedJob = new JobKey(QuartzScheduler.QB_JOB, QuartzScheduler.QB_GROUP);
}
[SetUp]
public void setup() {
dummy = new ControllerStub();
target = new QuartzScheduler(dummy, QuartzScheduler.QB_TRIGGER, QuartzScheduler.QB_JOB, QuartzScheduler.QB_GROUP, 0, 1);
}
[TearDown]
public void teardown() {
target.GetScheduler().Clear();
}
[Test]
public void HasBeenScheduled() {
Assert.IsTrue(target.GetScheduler().CheckExists(expectedJob));
Assert.IsTrue(target.GetScheduler().IsStarted);
Assert.IsTrue(target.GetScheduler().GetJobGroupNames().Contains(QuartzScheduler.QB_GROUP));
Assert.True(target.GetTrigger().JobKey.Equals(expectedJob));
Assert.Null(target.GetTrigger().GetPreviousFireTimeUtc());
Assert.IsTrue(target.GetTrigger().GetMayFireAgain());
}
[Test]
public void CanBeRescheduled() {
target.Reschedule(1, 0);
string actual = target.GetTrigger().GetNextFireTimeUtc().ToString();
Assert.IsTrue(actual.Contains("1:00:00 AM"));
}
[Test]
public void CanBeExecuted() {
DateTimeOffset expected = DateTimeOffset.Now;
expected.AddMinutes(1);
Assert.IsTrue(target.GetScheduler().CheckExists(expectedJob));
target.Reschedule(expected.Hour, expected.Minute);
target.GetScheduler().TriggerJob(expectedJob);
Thread.Sleep(60500);
Assert.NotNull(target.GetScheduler().GetTrigger(new TriggerKey(QuartzScheduler.QB_TRIGGER)).GetPreviousFireTimeUtc());
}
}
}
我能找到作业无法激活的唯一失败原因是调度程序找不到默认构造函数。我已经尝试使用给定方法和默认构造函数进行测试,但测试仍然失败。
我不知道为什么会这样。谁能提供可能的建议?
编辑:
更奇怪的是,向 Execute()
方法添加 Console.Write()
语句会导致消息出现在控制台 window 中,但对 FakeController.fired
值的更改不会坚持下去,GetTrigger().GetPreviousFiringTimeUtc()
仍然 returns null!
编辑:(更新代码)
我已经将问题缩小到 Trigger 对象的构造; target.GetScheduler.TriggerJob(expectedJob)
将导致出现 Console.Write()
消息(尽管静态变量不会更改值)。但是,如所示安排作业将导致触发器永远不会触发。
问题在于触发器的调度机制的构造没有被正确编写。 ChronScheduleBuilder
要么工作不正常,要么以错误的方式实施。
最终的解决方案是用一个简单的间隔调度程序代替它,如下所示:
trigger = TriggerBuilder
.Create()
.WithIdentity(triggerKey)
.ForJob(jobKey)
.WithSchedule(SimpleScheduleBuilder
.Create()
.WithIntervalInHours(24)
.RepeatForever())
.StartAt(
new DateTimeOffset(
new DateTime(
DateTimeOffset.Now.Year,
DateTimeOffset.Now.Month,
DateTimeOffset.Now.Day,
syncHour,
syncMinute,
0,
DateTimeKind.Local)))
.Build();
我在我正在处理的 C# 项目中 运行 遇到了 Quartz(版本 2.2.4)的一个相当令人费解的问题,尽管我尽了最大的努力,但我还是没能解决解决问题。
该项目需要独立安排多个不同的任务;在这种情况下,我专门编写了一个 class 来处理 24 小时周期的作业设置。 class 应该为每个需要安排的任务实例化一次。
下面是有问题的对象:
namespace ProjectFront.Utilities {
public class QuartzScheduler {
public const string QB_TRIGGER = "qbTrigger";
public const string QB_JOB = "qbJob";
public const string QB_GROUP = "qbGroup";
private IScheduler scheduler;
private ITrigger trigger;
private IJobDetail job;
private JobKey jobKey;
public QuartzScheduler(IJob controller, string triggerId, string jobId, string jobGroup, int syncHour, int syncMinute) {
StdSchedulerFactory schedulerFactory = new StdSchedulerFactory();
schedulerFactory.Initialize();
jobKey = new JobKey(jobId, jobGroup);
syncHour = getHour(syncHour);
syncMinute = getMin(syncMinute);
scheduler = schedulerFactory.GetScheduler();
scheduler.Start();
job = JobBuilder.Create(controller.GetType())
.StoreDurably()
.WithIdentity(jobKey)
.Build();
trigger = TriggerBuilder.Create()
.WithIdentity(triggerId)
.ForJob(jobKey)
.StartNow()
.WithSchedule(CronScheduleBuilder.DailyAtHourAndMinute(syncHour, syncMinute))
.Build();
scheduler.ScheduleJob(job, trigger);
scheduler.Start();
}
public void Reschedule(int syncHour, int syncMinute) {
scheduler.Standby();
syncHour = getHour(syncHour);
syncMinute = getMin(syncMinute);
TriggerKey triggerKey = trigger.Key;
trigger = TriggerBuilder.Create()
.WithIdentity(triggerKey)
.ForJob(jobKey)
.StartNow()
.WithSchedule(CronScheduleBuilder.DailyAtHourAndMinute(syncHour, syncMinute))
.Build();
scheduler.RescheduleJob(triggerKey, trigger);
scheduler.Start();
}
public IScheduler GetScheduler() {
return scheduler;
}
public ITrigger GetTrigger() {
return trigger;
}
private int getHour(int num) {
if (num < 4) {
return num + 20;
}
return num - 4;
}
private int getMin(int num) {
while (num >= 60) {
num -= 60;
}
return num;
}
}
}
我已经编写了几个 NUnit 测试来确保对象中的所有内容都能正常工作。它通过了其中两个,但总是让第三个失败;该工作肯定存在,甚至可以重新安排,但在调用 TriggerJob()
时静默失败。重新安排它然后等待它触发同样失败。
这些是测试:
namespace UnitTestProject.BackEnd_UnitTests.Util {
public class ControllerStub : IJob{
public ControllerStub() { }
public void Execute(IJobExecutionContext context)
{
Console.WriteLine("Fired!");
Console.WriteLine(DateTime.Now.ToString());
}
}
[TestFixture]
public class QBSchedulerTest {
public static int fired = 0;
private QuartzScheduler target;
private JobKey expectedJob;
private ControllerStub dummy;
[TestFixtureSetUp]
public void fixture() {
colAlias.LogManager.Adapter = new colAlias.Simple.TraceLoggerFactoryAdapter { Level = colAlias.LogLevel.Info };
expectedJob = new JobKey(QuartzScheduler.QB_JOB, QuartzScheduler.QB_GROUP);
}
[SetUp]
public void setup() {
dummy = new ControllerStub();
target = new QuartzScheduler(dummy, QuartzScheduler.QB_TRIGGER, QuartzScheduler.QB_JOB, QuartzScheduler.QB_GROUP, 0, 1);
}
[TearDown]
public void teardown() {
target.GetScheduler().Clear();
}
[Test]
public void HasBeenScheduled() {
Assert.IsTrue(target.GetScheduler().CheckExists(expectedJob));
Assert.IsTrue(target.GetScheduler().IsStarted);
Assert.IsTrue(target.GetScheduler().GetJobGroupNames().Contains(QuartzScheduler.QB_GROUP));
Assert.True(target.GetTrigger().JobKey.Equals(expectedJob));
Assert.Null(target.GetTrigger().GetPreviousFireTimeUtc());
Assert.IsTrue(target.GetTrigger().GetMayFireAgain());
}
[Test]
public void CanBeRescheduled() {
target.Reschedule(1, 0);
string actual = target.GetTrigger().GetNextFireTimeUtc().ToString();
Assert.IsTrue(actual.Contains("1:00:00 AM"));
}
[Test]
public void CanBeExecuted() {
DateTimeOffset expected = DateTimeOffset.Now;
expected.AddMinutes(1);
Assert.IsTrue(target.GetScheduler().CheckExists(expectedJob));
target.Reschedule(expected.Hour, expected.Minute);
target.GetScheduler().TriggerJob(expectedJob);
Thread.Sleep(60500);
Assert.NotNull(target.GetScheduler().GetTrigger(new TriggerKey(QuartzScheduler.QB_TRIGGER)).GetPreviousFireTimeUtc());
}
}
}
我能找到作业无法激活的唯一失败原因是调度程序找不到默认构造函数。我已经尝试使用给定方法和默认构造函数进行测试,但测试仍然失败。
我不知道为什么会这样。谁能提供可能的建议?
编辑:
更奇怪的是,向 Execute()
方法添加 Console.Write()
语句会导致消息出现在控制台 window 中,但对 FakeController.fired
值的更改不会坚持下去,GetTrigger().GetPreviousFiringTimeUtc()
仍然 returns null!
编辑:(更新代码)
我已经将问题缩小到 Trigger 对象的构造; target.GetScheduler.TriggerJob(expectedJob)
将导致出现 Console.Write()
消息(尽管静态变量不会更改值)。但是,如所示安排作业将导致触发器永远不会触发。
问题在于触发器的调度机制的构造没有被正确编写。 ChronScheduleBuilder
要么工作不正常,要么以错误的方式实施。
最终的解决方案是用一个简单的间隔调度程序代替它,如下所示:
trigger = TriggerBuilder
.Create()
.WithIdentity(triggerKey)
.ForJob(jobKey)
.WithSchedule(SimpleScheduleBuilder
.Create()
.WithIntervalInHours(24)
.RepeatForever())
.StartAt(
new DateTimeOffset(
new DateTime(
DateTimeOffset.Now.Year,
DateTimeOffset.Now.Month,
DateTimeOffset.Now.Day,
syncHour,
syncMinute,
0,
DateTimeKind.Local)))
.Build();