web-dev-qa-db-ja.com

クォーツ:jobs.xmlでのジョブの同時インスタンスの防止

これは本当に簡単なはずです。 Apache Tomcat 6.0.18の下で実行されているQuartzを使用しており、毎分実行されるスケジュールされたジョブを設定する jobs.xmlファイル があります。

私がやりたいのは、次のトリガー時間がくるときにジョブがまだ実行されている場合、新しいジョブを開始したくないので、古いインスタンスを完了させることができます。

Jobs.xmlでこれを指定する方法はありますか(同時インスタンスを防止)?

そうでない場合、アプリケーションの Job 実装内でインメモリシングルトンへのアクセスを共有できる方法はありますか(これは JobExecutionContext を介してですか?)自分? (前のインスタンスが実行されているかどうかを検出します)


update:ドキュメントをいじくり回した後、私が検討しているいくつかのアプローチがありますが、それらを動作させる方法がわからないか、問題があります。

  1. StatefulJob を使用します。これにより、同時アクセスが防止されます...しかし、それを使用した場合に他にどのような副作用が発生するかわかりません。また、次の状況を回避したいのです。

    トリガー時間は毎分、つまり、trigger#0 =時間0、trigger#1 = 60000msec、#2 = 120000、#3 = 180000などであり、timer 0でtrigger#0が130000msecのジョブを起動するとします。プレーンジョブでは、ジョブトリガー#0がまだ実行されている間にトリガー#1と#2が実行されます。 StatefulJobを使用すると、#0が130000で終了した直後に、トリガー#1と#2が順番に実行されます。それは望ましくありません。#1と#2は実行しないで、ジョブを実行する次のトリガーは#3(180000msec)で行われます。したがって、StatefulJobを使用して他の操作を行い、希望どおりに機能させる必要があるため、使用することの利点はあまりありません。

  2. TriggerListener を使用して、vetoJobExecution()からtrueを返します。

    インターフェイスの実装は簡単に思えますが、TriggerListenerの1つのインスタンスを宣言的にセットアップする方法を理解する必要があります。 xmlファイルのドキュメントが見つかりません

  3. Jobを実装するクラスが所有するstatic共有スレッドセーフオブジェクト(セマフォなど)を使用します。

    Tomcat/Quartzの下でstaticキーワードを介してシングルトンを使用するという考えは好ましくありません。副作用があるかどうかはわかりません。また、特定のジョブ定義に関連付けられているものだけを、真のシングルトンにしたくないのです。

  4. 独自の Trigger を実装します。これは SimpleTrigger を拡張し、独自のTriggerListenerを実行できる共有状態を含みます。

    繰り返しますが、標準の<trigger><simple>...</simple></trigger>ではなく、このトリガーを使用するようにXMLファイルをセットアップする方法がわかりません。

42
Jason S

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
    }
18
dimitrisli

別の簡単なソリューションがあります。ジョブには、複数の同時インスタンスの実行を防ぐDisallowConcurrentExecutionの注釈を付けることができます。 docs here を参照してください。

リンクは壊れ続けるので、関連するサンプルを次に示します。

@DisallowConcurrentExecution
public class ColorJob implements Job {
60
Ari Maniatis

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;
                }

            }

現在、ジョブコンテキストを取得し、同じトリガーを持つ以前のジョブインスタンスがあるかどうかを確認します。この場合、リターンでスキップします。

23
Sezin Karli

ジョブクラスに StatefulJob を実装させることで、現在の実行中のジョブが完了する前に他のジョブが開始されないようにすることで、同様のことを達成しました。

それが役立つことを願っています;)

PD:JBossを使用して実装しましたが、違いはないと思います。

5
Ramses

ジョブをStatefulJobとして設定し、作成するトリガーごとに、ジョブのMisfireInstructionを設定して、ジョブが失敗した場合に起動しないようにできますか?どのタイプのジョブを使用しているかはわかりませんが、トリガータイプで使用できるmisfireInstructionsについて調査する必要があります。

ありがとう

4
DMan

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);
    }
    ...
}
3
Paul Vargas

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;
    }

}

すべてのJobExecutionsに単一のインスタンスが使用されている場合、scaramoucheのソリューションは失敗します(実行ごとにnewInstance()を呼び出すのではなく、カスタムJobFactoryクラスを使用してシングルトンを返します)

1
Niranjan