石英:防止jobs.xml中的作业的并发实例
这应该很容易。 我使用的是在Apache Tomcat 6.0.18下运行的Quartz,而且我有一个jobs.xml文件 ,它可以设置每分钟运行的计划作业。
我想要做的是,如果在下一次触发时间到来的时候,工作还在运行,我不想开始新的工作,所以我可以让旧的实例完成。
有没有办法指定这在jobs.xml(防止并发实例)?
如果不是的话,有没有办法在我的应用程序的Job实现中(这是通过JobExecutionContext ?)共享访问内存中的单例,所以我可以自己处理并发? (并检测前一个实例是否正在运行)
更新:在文档中陷入困境之后,我正在考虑一些方法,但要么不知道如何让它们工作,或者有问题。
-
使用StatefulJob 。 这可以防止并发访问…但我不知道如果我使用它会发生什么其他的副作用,我也想避免以下情况:
假设触发时间是每一分钟,即触发#0 =在时间0,触发#1 = 60000msec,#2 = 120000,#3 = 180000等,并且在时间0触发#0触发我的工作需要130000msec。 用简单的Job,当作业触发器#0仍在运行时,它将执行触发器#1和#2。 使用StatefulJob,这将执行触发器#1和#2,在#0在130000结束后立即执行。我不想要那个,我想要#1和#2不运行,并且下一个运行作业的触发器应该发生在#3(180000msec)。 所以我仍然需要使用StatefulJob来做其他事情,以便按照我想要的方式工作,所以我没有看到使用它的好处。
-
使用TriggerListener从vetoJobExecution()返回true。
虽然实现接口看起来很简单,但我必须弄清楚如何以声明方式设置TriggerListener的一个实例。 找不到xml文件的文档 。
-
使用实现Job的我的类拥有的
static
共享线程安全对象(例如信号量或其他)。我不喜欢在Tomcat / Quartz中通过
static
关键字使用单例的想法,不确定是否有副作用。 另外,我真的不希望他们成为真正的单身人士,只是与特定的工作定义有关。 -
实现我自己的触发器 ,它扩展了SimpleTrigger,并包含可以运行自己的TriggerListener的共享状态。
再一次,我不知道如何设置XML文件来使用这个触发器,而不是标准的
<trigger><simple>...</simple></trigger>
。
当你的Quartz工作醒来时,你可以这样做:
JobDetail existingJobDetail = sched.getJobDetail(jobName, jobGroup); if (existingJobDetail != null) { List<JobExecutionContext> currentlyExecutingJobs = (List<JobExecutionContext>) sched.getCurrentlyExecutingJobs(); for (JobExecutionContext jec : currentlyExecutingJobs) { if(existingJobDetail.equals(jec.getJobDetail())) { //String message = jobName + " is already running."; //log.info(message); //throw new JobExecutionException(message,false); } } //sched.deleteJob(jobName, jobGroup); if you want to delete the scheduled but not-currently-running job }
还有另一个更简单的解决scheme。 可以给作业一个DisallowConcurrentExecution的注释,它可以防止多个并发的实例在运行。 在这里看到文档。
链接不断突破,这里是相关的示例。
@DisallowConcurrentExecution public class ColorJob implements Job {
dimitrisli的答案不完整,所以这里是我的。
当Quartz Job醒来时,它返回JobExecutionContext给你。 我假设你想用相同的触发器跳过作业。
List<JobExecutionContext> jobs = jobExecutionContext.getScheduler().getCurrentlyExecutingJobs(); for (JobExecutionContext job : jobs) { if (job.getTrigger().equals(jobExecutionContext.getTrigger()) && !job.getJobInstance().equals(this)) { logger.info("There's another instance running, so leaving" + this); return; } }
我们得到当前的作业上下文,并检查是否有一个以前的作业实例具有相同的触发器。 如果是这种情况,我们只是跳回来。
我完成了类似的工作,使得我的工作类实现了StatefulJob ,它确保在当前正在运行的作业结束之前不会启动其他作业。
希望有所帮助;)
PD:我使用JBoss实现了它,但是我不认为这有什么区别。
你可以把这个工作设置成一个有状态的工作吗?对于你创build的每一个触发器,设置这个工作的失火指令,如果错过了,就不会触发? 不知道你正在使用什么types的工作,但你必须做一些调查你的触发器types可用的失火指示。
感谢:D
对scaramouche的解决scheme有轻微的变化。
List<JobExecutionContext> jobs = jobExecutionContext.getScheduler().getCurrentlyExecutingJobs(); for (JobExecutionContext job : jobs) { if (job.getTrigger().equals(jobExecutionContext.getTrigger()) && !job.getFireInstanceId().equals(jobExecutionContext.getFireInstanceId()) { logger.info("There's another instance running, so leaving" + this); return; } }
当所有JobExecutions有一个实例时,scaramouche解决scheme失败(使用自定义JobFactory类返回单例,而不是每次执行都调用newInstance())
如果你正在使用org.springframework.scheduling.quartz.QuartzJobBean
:
protected void executeInternal(JobExecutionContext context) throws JobExecutionException { try { Scheduler scheduler = context.getScheduler(); List<JobExecutionContext> jobs = scheduler.getCurrentlyExecutingJobs(); for (JobExecutionContext job : jobs) { if (job.getTrigger().equals(context.getTrigger()) && job.getJobDetail() != context.getJobDetail()) { LOG.warn("Ignored!"); return; } } ... } catch (SchedulerException e) { LOG.error("What a luck! :'(", e); } ... }