2016-05-11 12 views
0

Related: Quartz Clustering - triggers duplicated when the server startsは、私はJavaベースのクラスタ環境でスケジュールされたジョブを管理するためにクォーツスケジューラを使用しているクラスタ環境

にクォーツトリガーの作成します。ある時点では、クラスタにはいくつかのノードがあり、すべてのノードが接続するpostgresqlデータベースのデータストアに基づいてQuartzを実行します。

インスタンスが初期化されると、それはジョブを作成または更新しようとすると、このコードを実行することにより、石英データストアにトリガ:

private void createOrUpdateJob(JobKey jobKey, Class<? extends org.quartz.Job> clazz, Trigger trigger) throws SchedulerException { 
    JobBuilder jobBuilder = JobBuilder.newJob(clazz).withIdentity(jobKey); 
    if (!scheduler.checkExists(jobKey)) { 
     // if the job doesn't already exist, we can create it, along with its trigger. this prevents us 
     // from creating multiple instances of the same job when running in a clustered environment 
     scheduler.scheduleJob(jobBuilder.build(), trigger); 
     log.error("SCHEDULED JOB WITH KEY " + jobKey.toString()); 
    } else { 
     // if the job has exactly one trigger, we can just reschedule it, which allows us to update the schedule for 
     // that trigger. 
     List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey); 
     if (triggers.size() == 1) { 
      scheduler.rescheduleJob(triggers.get(0).getKey(), trigger); 
      return; 
     } 

     // if for some reason the job has multiple triggers, it's easiest to just delete and re-create the job, 
     // since we want to enforce a one-to-one relationship between jobs and triggers 
     scheduler.deleteJob(jobKey); 
     scheduler.scheduleJob(jobBuilder.build(), trigger); 
    } 
} 

このアプローチは、多くの問題を解決する:

  1. 環境が正しく構成されていない場合(つまり、ジョブ/トリガーが存在しない場合)は、起動する最初のインスタンスによって作成されます
  2. ジョブはすでに存在しますが、 bは7分ごとに実行され、5分ごとに実行されます)、新しいトリガーを定義することができ、再デプロイメントによってデータベース内のトリガーが再スケジュールされます。
  3. ジョブのインスタンスが1つだけ作成されます。ジョブ自体によって定義された指定されたJobKeyによって常にジョブを参照します。これは、クラスタ内のノード数や展開回数にかかわらず、ジョブ(および関連するトリガー)が正確に1回作成されることを意味します。

これはすべてうまくいいですが、2つのインスタンスがまったく同時に開始された場合の競合状態が懸念されます。このコードの周りには、クラスタ内のすべてのノードが尊重するグローバルロックが存在しないため、2つのインスタンスが同時にオンラインになると、重複するジョブやトリガが発生する可能性があります。

クラスタ環境でQuartzジョブとトリガを自動的に定義するベストプラクティスはありますか?または自分のロックを設定する必要がありますか?

答えて

1

Quartzでこれを行うより良い方法があるかどうかはわかりません。しかし、すでにRedisまたはMemcacheを使用している場合は、すべてのインスタンスでよく知られているキーに対してatomic incrementを実行することをお勧めします。あなたが貼り付けコードが毎時クラスタごとに1つのジョブだけを実行することになっている場合は、次の操作を行うことができます:

long timestamp = System.currentTimeMillis()/1000/60/60; 
String key = String.format("%s_%d", jobId, timestamp); 

// this will only be true for one instance in the cluster per (job, timestamp) tuple 
bool shouldExecute = redis.incr(key) == 1 

if (shouldExecute) { 
    // run the mutually exclusive code 
} 

タイムスタンプは、あなたの仕事は、このジョブを実行するために競合している内を移動するウィンドウを提供します。

+0

これは、Quartzが同じ仕事のために複数のトリガーを作成するのを妨げないという意味で、私が探していたものではありませんが、それらのトリガーの1つだけが確実に実行されます。与えられたウィンドウ、私はそれが問題を解決すると思うので、ラウンドアバウトのような方法です。 – MusikPolice

0

私はほぼ同じ問題を抱えていました。クラスター化された環境で、ソフトウェアバージョンごとにトリガーとジョブを正確に1回作成する方法です。クラスタノードの1つを起動時にリードノードに割り当て、Quartzジョブを再作成するようにして、この問題を解決しました。リードノードは、まず実行中のソフトウェアのgitリビジョン番号をデータベースに挿入します。他のノードは、リードノードによって作成されたQuartz構成を使用します。 https://github.com/perttuta/quartz

関連する問題