Java 石英:防止jobs.xml 中作业的并发实例
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2676295/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
quartz: preventing concurrent instances of a job in jobs.xml
提问by Jason S
This should be really easy. I'm using Quartz running under Apache Tomcat 6.0.18, and I have a jobs.xml filewhich sets up my scheduled job that runs every minute.
这应该很容易。我正在使用在 Apache Tomcat 6.0.18 下运行的 Quartz,并且我有一个jobs.xml 文件,它设置了我每分钟运行的预定作业。
What I would like to do, is if the job is still running when the next trigger time rolls around, I don't want to start a new job, so I can let the old instance complete.
我想做的是,如果在下一个触发时间到来时作业仍在运行,我不想开始新作业,所以我可以让旧实例完成。
Is there a way to specify this in jobs.xml (prevent concurrent instances)?
有没有办法在 jobs.xml 中指定它(防止并发实例)?
If not, is there a way I can share access to an in-memory singleton within my application's Jobimplementation (is this through the JobExecutionContext?) so I can handle the concurrency myself? (and detect if a previous instance is running)
如果没有,有没有办法在我的应用程序的Job实现中共享对内存中单例的访问(这是通过JobExecutionContext吗?)这样我就可以自己处理并发?(并检测前一个实例是否正在运行)
update:After floundering around in the docs, here's a couple of approaches I am considering, but either don't know how to get them to work, or there are problems.
更新:在文档中挣扎之后,这里有几种我正在考虑的方法,但要么不知道如何让它们工作,要么存在问题。
Use StatefulJob. This prevents concurrent access... but I'm not sure what other side-effects would occur if I use it, also I want to avoid the following situation:
Suppose trigger times would be every minute, i.e. trigger#0 = at time 0, trigger #1 = 60000msec, #2 = 120000, #3 = 180000, etc. and the trigger#0 at time 0 fires my job which takes 130000msec. With a plain Job, this would execute triggers #1 and #2 while job trigger #0 is still running. With a StatefulJob, this would execute triggers #1 and #2 in order, immediately after #0 finishes at 130000. I don't want that, I want #1 and #2 not to run and the next trigger that runs a job should take place at #3 (180000msec). So I still have to do something else with StatefulJob to get it to work the way I want, so I don't see much of an advantage to using it.
Use a TriggerListenerto return true from vetoJobExecution().
Although implementing the interface seems straightforward, I have to figure out how to setup one instance of a TriggerListener declaratively. Can't find the docs for the xml file.
Use a
static
shared thread-safe object (e.g. a semaphore or whatever) owned by my class that implements Job.I don't like the idea of using singletons via the
static
keyword under Tomcat/Quartz, not sure if there are side effects. Also I really don't want them to be true singletons, just something that is associated with a particular job definition.Implement my own Triggerwhich extends SimpleTriggerand contains shared state that could run its own TriggerListener.
Again, I don't know how to setup the XML file to use this trigger rather than the standard
<trigger><simple>...</simple></trigger>
.
使用StatefulJob。这可以防止并发访问......但我不确定如果我使用它会发生什么其他副作用,我也想避免以下情况:
假设触发时间是每分钟一次,即 trigger#0 = 在时间 0,trigger #1 = 60000msec,#2 = 120000,#3 = 180000 等,并且 trigger#0 在时间 0 触发我的工作,这需要 130000 毫秒。对于普通作业,这将在作业触发器 #0 仍在运行时执行触发器 #1 和 #2。使用 StatefulJob,这将在 #0 在 130000 结束后立即按顺序执行触发器 #1 和 #2。我不希望那样,我希望 #1 和 #2 不运行,并且运行作业的下一个触发器应该发生在#3(180000 毫秒)。所以我仍然需要对 StatefulJob 做一些其他的事情才能让它按照我想要的方式工作,所以我看不到使用它有什么好处。
使用TriggerListener从 vetoJobExecution() 返回 true。
尽管实现该接口看起来很简单,但我必须弄清楚如何以声明方式设置 TriggerListener 的一个实例。找不到 xml 文件的文档。
使用
static
我的类拥有的实现 Job的共享线程安全对象(例如信号量或其他)。我不喜欢通过
static
Tomcat/Quartz 下的关键字使用单例的想法,不确定是否有副作用。此外,我真的不希望他们成为真正的单身人士,只是与特定工作定义相关的东西。实现我自己的触发器,它扩展了SimpleTrigger并包含可以运行自己的 TriggerListener 的共享状态。
同样,我不知道如何设置 XML 文件以使用此触发器而不是标准的
<trigger><simple>...</simple></trigger>
.
采纳答案by dimitrisli
when your Quartz job wakes up you can do:
当您的 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
}
回答by Ramses
I accomplished something similar making my job classes implement StatefulJob, which ensures that no other jobs starts before the current running job finishes.
我完成了类似的事情,使我的作业类实现StatefulJob,这确保在当前运行的作业完成之前没有其他作业启动。
Hope that helps ;)
希望有帮助;)
PD: I implemented it using JBoss... but I don't think that makes any difference.
PD:我使用 JBoss 实现了它……但我认为这没有任何区别。
回答by DMan
could you set the job as a StatefulJob, and for each trigger you create set the MisfireInstruction for the job to not fire if it is missed? Not sure what type of job you are using but you'll have to do some research into the misfireInstructions that are available for your trigger type.
您能否将作业设置为 StatefulJob,并为您创建的每个触发器设置 MisfireInstruction 以使作业在错过时不触发?不确定您正在使用什么类型的作业,但您必须对适用于您的触发器类型的 misfireInstructions 进行一些研究。
Thanks, D
感谢:D
回答by Sezin Karli
dimitrisli's answer is not complete so here's mine.
dimitrisli 的回答不完整,所以这是我的。
When Quartz Job wakes up it returns its JobExecutionContext to you. I assume you want to skip jobs with the same trigger.
当 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;
}
}
We get currently job contexts and check if there's a previous job instance with the same trigger. If this is the case we just skip with return.
我们获取当前的作业上下文并检查是否存在具有相同触发器的先前作业实例。如果是这种情况,我们就直接跳过返回。
回答by Ari Maniatis
There is another simpler solution. The job can be given an annotation of DisallowConcurrentExecution which prevents multiple concurrent instances running. See docs here.
还有另一个更简单的解决方案。可以为作业提供 DisallowConcurrentExecution 注释,以防止多个并发实例运行。请参阅此处的文档。
The link keeps breaking so here is the relevant sample.
链接不断断开,所以这里是相关示例。
@DisallowConcurrentExecution
public class ColorJob implements Job {
回答by Niranjan
Slight variation to scaramouche's solution.
Scaramouche 的解决方案略有不同。
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;
}
}
scaramouche's solution fails when there is a single instance used for all JobExecutions( returning the singleton using a custom JobFactory class, rather than calling newInstance() for each execution)
当所有 JobExecutions 使用单个实例时,scaramouche 的解决方案失败(使用自定义 JobFactory 类返回单例,而不是为每次执行调用 newInstance())
回答by Paul Vargas
If you are using org.springframework.scheduling.quartz.QuartzJobBean
:
如果您正在使用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);
}
...
}