クォーツソースコード分析(上)



Quartz Source Code Analysis



記事のディレクトリ

アイデア

Quartzフレームワークは、最近のプロジェクトで使用されています。タイミングタスクのスケジューリングフレームワークとして、Springとの完全な融合であろうと、Javaコードを直接使用するものであろうと、非常にシンプルで安定していると言えます。 ある日、Quartzフレームワークがない場合、純粋なJavaコードを使用して時限タスクスケジューリングフレームワークをどのように実装すればよいのかと突然思いました。

  • アイデア1:私はすぐにデザインのアイデアを思いつきました。 現在の時点とタスクの時点を継続的にポーリングするには、個別のスケジューリングスレッドが必要です。タスクの出発時刻に達すると、スレッドが開かれ、引き渡す必要のあるタスクがそのスレッドに送信されます。 。ただし、そうするとスレッドが多すぎる可能性があり、CPUリソースを占有するためにスレッドを継続的に循環させることは合理的ではありません。
  • アイデア2:初版のアイデアに基づいて、 別のスケジューリングスレッドは、タスクが次にトリガーされてスリープに入るのを待ちます。スレッドのスリープ時間は、次のトリガーの時間差に応じて決定され、タスクはスレッドプールに送信されます。 タスクが多すぎるために発生するスレッドの爆発現象を回避できます。

だから私はQuartzのソースコードを見るしかありませんでした(もっと釣りをしています...)、そしてQuartzのコアデザインのアイデアが私の2番目のバージョンのアイデアに似ていることがわかりました、そしてナンセンスは大多数ではありません、ソースコードを見せてください!



2.Quartz全体のアーキテクチャ

最初に写真を盗む:
画像
Quartzフレームワークのスリーピースセット:

  1. ジョブ:実行する必要のあるタスク。
  2. トリガー:トリガー、ジョブをトリガーする特定の時間を指定できます
  3. スケジューラー(コア): 統合スケジューラ、ジョブ、トリガーがパラメータとして渡され、スケジューラはタスクのトリガーとタイムスケジューリングを完了します

タスクの送信からタスクのスケジュールされた実行までのQuartzは、これら3つのセットに依存して完了すると言えます。もちろん、Quartzの基本的な使用法をマスターしていると思いますが、これらのコンポーネントを分析します。一つずつ。



2.1ジョブとJobDetail

Quartzでタスクを送信する場合、 自己実装が必要ですJobインターフェースのクラス。定期的にトリガーされるコードを上書きするだけで済みます。executeメソッド

/ / Class that implements the Job interface public class MyJob implements Job { @Override public void execute(JobExecutionContext context) throws JobExecutionException { System.out.println('this is a test job!') } }

Quartzを使用する場合、必要なビジネスを完了するためにJobインターフェースを実装しました。executeメソッドが必要ですJobインターフェースを実装する必要がある唯一の方法。

実際、Quartzは、ジョブのパッケージ化をより複雑にする必要があります。たとえば、Spring内部パッケージで生成されますBean同じJob最終的にはa JobDetailImplにパッケージ化されますオブジェクト。



JobDetailImplはいJobDetailインターフェースのデフォルトの実装クラス。これらのプロパティのいくつかを見ることができます。
画像
JobDetailImplオブジェクトはQuartzフレームワークで使用されます。SpringBeanオブジェクトはSpringフレームワークで使用されます。追加Jobパッケージに基づきます。

on JobDetailオブジェクト生成、Quartz使用 ビルダーモード

見ることができますJobBuilderクラスの構造、明らかにこれはa Builderクラスです。

画像

public JobDetail build() { JobDetailImpl job = new JobDetailImpl() job.setJobClass(jobClass) job.setDescription(description) if(key == null) key = new JobKey(Key.createUniqueName(null), null) job.setKey(key) job.setDurability(durability) job.setRequestsRecovery(shouldRecover) if(!jobDataMap.isEmpty()) job.setJobDataMap(jobDataMap) return job }

**コアメソッドは上記のとおりです。ビルダーモードは、Android側でより一般的に使用されます。ここでの利点は、生成する方が便利なことです。JobDetailオブジェクト、引き渡されるScheduler使用。 ** ps: ビルダーモードについて-新人チュートリアル

2.2トリガー

TiggerトリガーロールとしてのQuartzでは、渡すことができますTriggerオブジェクトは、タスクスケジューリングの頻度設定、時間設定などを実装するために使用されます。

Trigger Quartzには2つの一般的な使用法があり、1つはSimpleTriggerインターフェースによって実装された単純なトリガーに基づいています。これらのトリガールールは比較的単純です。これらは通常、特定の間隔で実行されます。インターフェイスによって実装されたCronTrigger cron式トリガーに基づく単語もあります。 cron式は、より強力なトリガールールを実装できます。 Quartzの友達は間違いなくCron式にさらされます(Linuxにさらされていたに違いありません) 。ここでは、より単純に見えますSimpleTrigger

TriggerインスタンスおよびJobDetail例のように、これらはBuilderパターンを使用して作成されます。実際の実装クラスはSimpleTriggerImpl classです。

見てみましょうSimpleTriggerクラス継承図:
画像
実際、トリガーの役割の説明は、QuartzにJavaDocが付属しているので、私が言ったよりも明確なはずです。

/** * A {@link Trigger}

Jobクラスには、トリガー名、グループ、開始時間、実行回数、実行間隔、その他の多くの属性など、多くのプロパティがあります。SimpleTriggerImplクラスと同様に、これらのプロパティQuartzフレームワークによって使用されます。

これは、sum JobDetailImpl密接に関連するクラス、つまりTrigger Classの特別な説明です。明らかに、これはBuilderモードのクラスでもあります。 実際には、a ScheduleBuilder使用されるスケジュールを生成し、パラメーターとして渡されますTriggerクラス内 ここでの注記も非常に明確です。TriggerBuilderトリガーのトリガー時間を定義するために使用されます。 (もちろんScheduleBuilder別の変換ロジックのセットです。トウモロコシの式をタイムラインに変換する必要があります)

画像

2.3 JobStore

ここに古典的な質問があります:私は今コーヒーを持っています、ミルクと一緒に、どうすればコーヒーミルクを手に入れることができますか?

はい、カップが必要です。

今と同じようにCronTrigger持っているJobそれらをどのように関連付けて保存しますか? ?答えはTriggerクラス、 JobStore確立されているJobStore with JobDetail関連付けられたキークラスも保存されているTrigger with JobDetailタイプ 。 Quartz内には2つの実装クラスがあります。

  • メモリに格納され、対応するクラスTiggerこのストレージクラスのプロパティは、プロジェクトの再起動後にデータがクリアされることです。
  • JDBCインターフェースを使用してデータベースに格納され、対応するRAMJobStore ClassおよびJobStoreTX Classesは、それぞれQuartzスタンドアロン永続性およびクラスター永続性デプロイメントのデータベース操作に対応します。

ここで分析しますJobStoreCMTこのクラスは手動で作成する必要はありません。構成ファイルでデータベース構成が有効になっていない場合、Quartzはデフォルトでこのクラスを使用します。

Quartzフレームワークでは、RAMJobStoreメソッドの送信scheduleJob with Job、achieve Trigger with Jobに関連する2つの重要なステップがあります。

  1. Triggerインスタンス設定Triggerここでは各Quartzフレームワークに注意を払う必要がありますJobKey一意になりますJob識別するために
  2. storage JobKey with JobDetial To Trigger Quartzには、保存する方法が2つあります。1つはメモリに保存され、もう1つは使用されます。JobStoreデータベースに保存されます。

画像
以下はJDBCクラスのコアメソッドRAMJobStoreソースコード:

storeJobAndTrigger

シンプルで明確、最初に保存public void storeJobAndTrigger(JobDetail newJob, OperableTrigger newTrigger) throws JobPersistenceException { storeJob(newJob, false) storeTrigger(newTrigger, false) } もう一度保存Jobもちろん、実際にはTrigger with storeJobこのメソッドは、ストレージオブジェクトメソッドの実際の実装です。

次の質問が来ます。ストレージを設計する場合storeTrigger with Jobメモリ内の構造を関連性のあるものにしながら、Javaでのどのコンテナ実装を選択しますか? 2年生について考えさせてください。私たちのカリキュラム設計は、SQLタイプのデータベースを実装しています。ネストのいくつかのレイヤーを使用します。Triggerストレージ構造を作成しました。 もちろんここHashMap入れ子RAMJobStore満たすHashMapJobマッピング関係。

Trigger

そうです、あなたが思うほど簡単です。// core storage container protected Map<JobKey, List<TriggerWrapper>> triggersByJob = new HashMap<JobKey, List<TriggerWrapper>>(1000) 前に述べたように、Quartzフレームワークではグローバルに一意です(分散展開Quartzはグローバルに一意ですが、使用されますJobKeyクラス)、 UUIDはいListリスト、つまり1つTrigger複数Jobトリガーに対応できます。

保存方法を見てみましょう。

Trigger

public void storeTrigger(OperableTrigger newTrigger, boolean replaceExisting) throws JobPersistenceException { TriggerWrapper tw = new TriggerWrapper((OperableTrigger)newTrigger.clone()) synchronized (lock) { // ...... omit part of the code // core function code: add to triggers by job List<TriggerWrapper> jobList = triggersByJob.get(tw.jobKey) if(jobList == null) { jobList = new ArrayList<TriggerWrapper>(1) triggersByJob.put(tw.jobKey, jobList) } jobList.add(tw) // ...... part of the code is omitted } } ここでのキーワードは、マルチスレッド環境でのデータのセキュリティを確保することです。実際、データを保存するだけで非常に簡単ですが、synchronized後で説明します。JobStoreコアコードは非常に重要な役割を果たします。

3.最初の要約

これまで、Quartzのコア作業コードを分析していません。理解してくださいScheduler with Job作成、パッケージ化、保存、もちろん、これも非常に重要です。 ことわざにあるように、賢い女性はご飯なしではいられません。Trigger with Job原材料もクォーツの重要な部分であるため、次の記事を分析しますTrigger出典Quartzのコアコードであるコード スレッドモデル、設計アイデアを含む、いくつかの分析を行います。

Quartzがどのようにそれを実現するかという私たちの考えの1つについては、こちらもご覧ください。


参考資料: