Quartz.NET:如果在计划成熟时运行作业,则需要在 iStatefulJob 实例上使用 CronTrigger 来“延迟而不是跳过”
你好,你的友好邻居 Quartz.NET n00b 回来了!
我有一个在基于 Quartz.NET CronTrigger 的调度方案上运行 iStatefulJob 实例的 Windows 服务...用于调度作业的 CRON 字符串:“0 0/1 * * * ? *”
一切都很好。但是,如果我有一个作业设置为在每分钟的 X:00 标记运行,并且该作业恰好运行超过一分钟,我会注意到后续作业在作业完成后立即运行执行,而不是等到下一个计划运行,有效地“排队”而不是仅仅跳过作业直到下一个计划运行。
我在触发器中放入了一条“DONOTHING”的 CronTrigger MisfireInstruction,但是当作业超出其下一个计划执行计划时,会发生完全相同的情况。
如何让 iStatefulJob 实例仅跳过当前正在运行的计划执行触发器,而不是将其延迟到第一次执行完成?
我明确设置了trigger.MisfireInstruction = MisfireInstruction.CronTrigger.DoNothing;
...但是,对于计划每分钟运行一次需要 90 秒才能完成的作业,我不会“什么也不做”,而是会遇到以下执行日志:
- 作业在上午 9:00:00 运行,在上午 9:01:30 结束 < ;- 作业运行时间为 1:30
- 作业在上午 9:01:30 运行,于上午 9:03:00 结束 <- 应该在 9:01:00 运行的后续作业
- 作业在上午 9:04:00 运行,完成于9:05:30am <- 这不是应该在 9:03:00 运行吗?
- 作业在上午 9:05:30 运行,在上午 9:07:00 结束 <- 应该在 9:05:00 运行的后续作业 作业
- 在上午 9:08:00 运行,在上午 9:09:30 完成 <-这不应该在 9:07:00 运行吗?
...看起来它第一次正确运行,在分钟...随着 90 秒作业执行时间到期,延迟 30 秒,然后,而不是等到下一个整分钟,而是在 30 秒处立即执行标记...更加奇怪的是,它随后在分钟标记上完成了第二个作业,但要等到下一个分钟标记才执行,而不是来回运行它...
看起来它几乎每次都能正常工作RUN,当它没有在 :30 标记上运行时...
让作业不延迟/排队,而是跳过直到它空闲且下一个时间表成熟的最佳方法是什么?
编辑:我尝试使用相同的 DONOTHING 触发失火指令返回 iJobs 而不是 iStatefulJobs,但尽管先前的执行仍然处于活动状态,但该作业每分钟都会执行。如果它当前正在使用 iJob 或 iStatefulJob 运行,我似乎无法让它跳过计划的运行...
编辑#2:我认为我的触发器永远不会失火,这就是为什么 DoNothing 作为失火指令是无用的。 ..鉴于这种情况,我想我需要另一种机制来检测计划的作业实例是否正在运行,以确保作业跳过其下一次执行,直到其下一个计划时间,而不是将其延迟到第一个实例完成...
编辑3:我尝试向 iStatefulJob jobdatamap 添加一个名为“IsRunning”的元素...当执行序列开始时,我将其设置为 TRUE,然后在作业完成后将其返回为 false。在执行之前,它会检查该元素,该元素显然在作业之间保留,如果检测到它是真的,则提前退出执行(记录“作业已跳过!”)...不幸的是,这不起作用,原因可能很明显:如果作业按照上面的项目符号计划运行,则作业永远不会与其自身同时运行,因为它会延迟运行直到作业结束,因此此检查是无用的。根据文档,从 iStatefulJob 返回 iJob 在这里没有帮助,因为 jobdatamap 仅在有状态作业类型的作业之间保留...
我仍然没有解决如何跳过计划作业而不是延迟它直到当前迭代完成...如果有人有想法,你就是救星! :)
Greetings, your friendly neighborhood Quartz.NET n00b is back!
I have a Windows Service running iStatefulJob instances on a Quartz.NET CronTrigger based schedule scheme... The CRON String used to schedule the job: "0 0/1 * * * ? *"
Everything works great. However, if I have a job that is set to run, say, at the X:00 mark of every minute, and that job happens to run for MORE than a minute, I notice that the subsequent job runs IMMEDIATELY after the job is finished executing, rather than waiting until its next scheduled run, effectively "queuing" up instead of merely skipping the job till it's next scheduled run.
I put in the trigger a CronTrigger MisfireInstruction of DONOTHING, but the exact same thing happens when a job overruns its next scheduled execution schedule.
How do I get an iStatefulJob instance to merely SKIP a scheduled execution trigger if it is currently running, rather than have it delay it until the first execution completes?
I explicitly set the trigger.MisfireInstruction = MisfireInstruction.CronTrigger.DoNothing;
...But instead of "doing nothing", for a job scheduled to run every minute that takes 90 seconds to complete, I experience the following execution log:
- Job runs at 9:00:00am, finishes at 9:01:30am <- job runs for 1:30
- Job runs at 9:01:30am, finishes at 9:03:00am <- subsequent job that should have run at 9:01:00
- Job runs at 9:04:00am, finishes at 9:05:30am <- shouldn't this one have run at 9:03:00?
- Job runs at 9:05:30am, finishes at 9:07:00am <- subsequent job that should have run at 9:05:00
- Job runs at 9:08:00am, finishes at 9:09:30am <- shouldn't this have run at 9:07:00?
... it seems like it runs correctly the first time, on the minute... delays for 30 seconds as the 90 second job execution time expires, and then, instead of waiting till the NEXT full minute, EXECUTES IMMEDIATELY at the 30 second mark... Doubly odd, is that it then finishes the SECOND job on the minute mark, but waits till the NEXT minute mark to execute instead of running it back-2-back...
Pretty much seems like it works correctly EVERY OTHER RUN, when it is not running on the :30 marks...
What's the best way to get a job not to delay/queue, but to just SKIP until it is idle and the next schedule matures?
EDIT: I tried going back to iJobs instead of iStatefulJobs using the same DONOTHING trigger misfire instruction, but the job executes EVERY MINUTE despite the prior execution being still active. I can't seem to get it to skip a scheduled run if it is currently running with either iJob or iStatefulJob...
EDIT#2: I think that my triggers are NEVER misfiring, which is why DoNothing as a misfire instruction is useless... Given that's the case, I guess I need another mechanism to detect if a job instance of a schedule is running to ensure the job SKIPS its next execution until its following scheduled time rather than delaying it until first instance completion...
EDIT3: I tried adding an element to the iStatefulJob jobdatamap called "IsRunning"... I set it to TRUE when the execute sequence starts, and then return it to false after job completion. Before executing, it checks the element, which is apparently persisted between jobs, and prematurely quits the execution (logging "JOB SKIPPED!") if it detects it to be true... This unfortunately doesn't work, for probably obvious reasons: If the jobs are running following the bulleted schedule above, then the job is never SIMULTANEOUSLY running along with itself, as it is delaying the run till the job ends, so this check is useless. According to documentation, returning to iJob from iStatefulJob would not help here as the jobdatamap is only persisted between jobs in the Stateful job type...
I still haven't solved how to SKIP a scheduled job instead of delaying it till it's current iteration completes... If anyone has ideas, you're a lifesaver! :)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
应该是RAMJobStore的misfireThreshold(http://quartznet.sourceforge.net/apidoc/topic2722. html)。
默认为 60 秒。因此,直到迟到超过 MisfiredThreshold 值时,作业才会被视为“失火”。
要解决该问题,只需降低此阈值(下面的代码设置为 1 毫秒):
它应该可以解决该问题。
It should be caused by misfireThreshold of RAMJobStore (http://quartznet.sourceforge.net/apidoc/topic2722.html).
It is 60 seconds by default. So job isn't considered as "misfired" until it is late for more than misfiredThreshold value.
To resolve the problem just decrease this threshold (below code sets to 1 ms):
It should resolve the issue.