1。Quartzタスクのスケジューリングの基本的な実装原則
Quartzは、Javaの実装に完全に基づいたOpenSymphonyによるタスクスケジューリングの分野におけるオープンソースプロジェクトです。優れたオープンソーススケジューリングフレームワークとして、Quartzには次の機能があります。
(1)さまざまなスケジューリング方法をサポートするなどの強力なスケジューリング機能は、さまざまな従来のニーズや特別なニーズを満たすことができます。
(2)タスクとスケジューリングの複数の組み合わせのサポート、スケジューリングの複数のストレージ方法のサポートなどの柔軟なアプリケーション方法。
(3)分散能力とクラスタリング機能であるTerracottaは、買収後に元の機能をさらに改善しました。この記事では、この部分になります。
1.1クォーツコア要素
Quartzタスクスケジューリングのコア要素は、スケジューラタスクスケジューラ、トリガートリガー、ジョブタスクです。トリガーとジョブはタスクスケジューリングのメタデータであり、スケジューラは実際にスケジューリングを実行するコントローラーです。
トリガーは、スケジューリング時間を定義するために使用される要素です。つまり、タスクが実行される時間に応じてどの時間ルールです。 Quartzには、Simpletrigger、Crontirgger、dateIntervaltrigger、およびnthincludeddaytriggerの4種類のトリガーがあります。これらの4つのトリガーは、エンタープライズアプリケーションのほとんどのニーズを満たすことができます。
ジョブは、スケジュールされたタスクを表すために使用されます。主に2種類のジョブがあります:ステートレスとステートフル。同じトリガーでは、ステートフルな仕事を並行して実行することはできません。最後にトリガーされたタスクが実行された後にのみ、次の実行をトリガーできます。ジョブには2つの主な属性があります。揮発性と耐久性。揮発性は、タスクがデータベースストレージに持続するかどうかを意味しますが、耐久性は、トリガー関連がないときにタスクが保持されるかどうかを意味します。値が真である場合、両方とも持続または保存されます。ジョブは複数のトリガーに関連付けることができますが、トリガーは1つのジョブのみを関連付けることができます。
スケジューラは、Scheduler Factory:DirectSchedulerFactoryまたはSTDSCHEDULERFACTORYによって作成されます。 DirectSchedulerFactoryは使用するのに十分な便利ではなく、多くの詳細な手動コーディング設定が必要であるため、2番目の工場であるStdschedulerFactoryがより頻繁に使用されます。スケジューラには、Remotembeanscheduler、RemoteScheduler、STDSChedulerの3つの主要なタイプがあります。
石英のコア要素間の関係を図1.1に示します。
図1.1コア要素関係図
1.2クォーツスレッドビュー
Quartzには、2つのタイプのスレッド、スケジューラスケジューラスレッドとタスク実行スレッドがあり、通常、タスク実行スレッドがスレッドプールを使用してスレッドのグループを維持します。
図1.2クォーツスレッドビュー
スケジューラには2つのメインスレッドがあります。通常のスケジューリングを実行するスレッドと、MisfiredTriggerを実行するスレッドです。通常のディスパッチスレッドポーリングは、すべてのトリガーが保存されます。トリガーする必要があるトリガーがある場合、つまり、次のトリガーの時間が到達した場合、タスク実行スレッドプールからアイドルスレッドを取得して、トリガーに関連付けられたタスクを実行します。ミスファイアスレッドは、すべてのトリガーをスキャンして、MisfiredTriggerがあるかどうかを確認します。もしそうなら、それはミスファイアポリシーに従って個別に処理されます(今すぐ火災するか、次の火災を待つ)。
1.3 Quartzジョブデータストレージ
クォーツのトリガーとジョブは、使用する前に保存する必要があります。 Quartzには2つのストレージ方法があります。RamjobstoreとJobStoresupportです。ここでは、Ramjobstoreが保存し、JobStoresupportストアはJDBCに基づいてデータベースでトリガーとジョブをトリガーします。 Ramjobstoreは非常に速いアクセスですが、システムが停止した後にすべてのデータが失われるため、ClusterアプリケーションでJobStoreSupportを使用する必要があります。
2。Quartzクラスター原理2.1 Quartzクラスターアーキテクチャ
Quartzクラスターの各ノードは、独立したクォーツアプリケーションであり、他のノードを管理します。これは、各ノードを個別に起動または停止する必要があることを意味します。 Quartzクラスターでは、独立したクォーツノードは別のノードまたは管理ノードと通信するのではなく、図2.1に示すように、同じデータベーステーブルを介して別のQuartzアプリケーションを認識します。
図2.1クォーツクラスターアーキテクチャ
2.2 Quartzクラスター関連のデータベーステーブル
Quartzクラスターはデータベースに依存するため、最初にQuartzデータベーステーブルを作成する必要があります。 Quartzリリースパッケージには、サポートされているすべてのデータベースプラットフォームのSQLスクリプトが含まれています。これらのSQLスクリプトは、<Quartz_home>/docs/dbtablesディレクトリに保存されています。ここで使用されているQuartzバージョン1.8.4には、合計12のテーブルがあります。テーブルの数は、バージョンによって異なる場合があります。データベースはMySQLであり、tables_mysql.sqlを使用してデータベーステーブルを作成します。すべての表を図2.2に示し、これらのテーブルの簡単な紹介を図2.3に示します。
図2.2 mysqlデータベースでQuartz 1.8.4で生成された表
図2.3クォーツデータテーブルの概要
2.2.1スケジューラステータステーブル(QRTZ_SCHEDULER_STATE)
説明:クラスターのノードインスタンス情報は、このテーブルの情報を定期的に読み取り、クラスター内の各インスタンスの現在のステータスを決定します。
instance_name:構成ファイルのorg.quartz.scheduler.instanceIdによって構成された名前。 Autoに設定すると、Quartzは物理マシン名と現在の時刻に基づいて名前を生成します。
last_checkin_time:最後のチェックイン時間
checkin_interval:チェックイン間隔時間
2.2.2トリガーアンドタスクアソシエーションテーブル(QRTZ_FIRED_TRIGGERS)
関連するジョブのトリガートリガーと実行情報に関連するステータス情報を保存します。
2.2.3トリガー情報テーブル(QRTZ_TRIGGERS)
trigger_name:トリガー名、ユーザーは自由に名前をカスタマイズでき、強制要件はありません
trigger_group:ユーザーが自由にカスタマイズできるトリガーグループの名前であり、強制要件はありません。
job_name:qrtz_job_detailsテーブルjob_nameの外部キー
job_group:qrtz_job_detailsテーブルjob_group外部キー
trigger_state:現在のトリガーステータスが取得するように設定されています。待機するように設定されている場合、ジョブはトリガーされません。
trigger_cron:クロン式を使用してトリガータイプ
2.2.4タスクの詳細表(QRTZ_JOB_DETAILS)
注:ジョブの詳細を保存すると、実際の状況に応じてテーブルをユーザーが初期化する必要があります
job_name:クラスター内のジョブの名前。ユーザーは、強制要件なしで自由に名前をカスタマイズできます。
job_group:ジョブがクラスターに属するグループの名前。ユーザーが自由にカスタマイズし、強制要件はありません。
job_class_name:クラスター内のジョブ実装クラスの完全なパッケージ名。 Quartzは、ClassPathへのこのパスに基づいてジョブクラスを見つけます。
is_durable:持続するかどうか、このプロパティを1に設定するかどうか、Quartzはデータベースにジョブを維持します
job_data:持続するジョブオブジェクトを保存するブロブフィールド。
2.2.5許可情報表(QRTZ_LOCKS)
注:図2.4に示すように、Tables_oracle.sqlには対応するDML初期化があります。
図2.4 Quartz許可情報表の初期化情報
2.3クラスター内のクォーツスケジューラ起動プロセス
Quartzスケジューラ自体は、クラスター化されていることに気付かず、スケジューラ用に構成されたJDBCジョブストアのみが知っています。 Quartzスケジューラが起動すると、JobstoreのSchedulerstarted()メソッドを呼び出します。これは、Jobstoreスケジューラに開始されたことを伝えます。 SchedulerStarted()メソッドは、JobStoreSupportクラスに実装されています。 JobStoreSupportクラスは、Quartz.Propertiesファイルの設定に基づいて、スケジューラインスタンスがクラスターに参加するかどうかを決定します。クラスターが構成されている場合、新しいクラスターマネージャークラスのインスタンスが作成され、初期化され、開始されます。 ClusterManagerは、Java.lang.Threadを継承するJobStoresupportクラスのインラインクラスであり、定期的に実行され、スケジューラインスタンスでチェックイン機能を実行します。また、スケジューラは、他のクラスターノードが失敗したかどうかを確認する必要があります。チェックイン操作の実行サイクルは、Quartz.Propertiesで構成されています。
2.4失敗したスケジューラノードの検出
スケジューラインスタンスがチェックインを実行すると、他のスケジューラインスタンスが予想されたときにチェックインされていないかどうかを確認します。これは、Scheduler_StateテーブルのLAST_CHEDK_TIME列に記録されたスケジューラの値がorg.quartz.jobstore.clustercheckinIntervalよりも早いかどうかを確認することによって決定されます。 1つ以上のノードが所定の時間にチェックインされていない場合、実行中のスケジューラは失敗したと想定します。
2.5失敗したインスタンスからのジョブの回復
ジョブを実行するときにShedulerインスタンスが失敗すると、別の作業スケジューラインスタンスがジョブを取得して再度実行する可能性があります。この動作を達成するには、JobDetailオブジェクトに設定されたジョブリカバリプロパティをtrue(job.setrequestsrecovery(true))に設定する必要があります。回復可能なプロパティがfalsに設定されている場合(デフォルトはfalse)、スケジューラがジョブの実行に失敗した場合、再実行されません。次のトリガー時間に別のスケジューラインスタンスによってトリガーされます。障害が各スケジューラのチェックイン間隔に依存した後にスケジューラインスタンスを検出できる速さ(つまり、org.quartz.jobstore.clustercheckinintervalは2.3で言及されています)。
3。Quartzクラスターインスタンス(Quartz+Spring)
3.1クォーツの問題と互換性のないスプリング
春は2.0.2以降、Quartzをサポートしなくなりました。具体的には、Quartz+SpringがQuartzのタスクをデータベースにインスタンス化すると、シリアル化可能なエラーが発生します。
<bean id = "jobtask"> <property name = "targetobject"> <ref bean = "quartzjob"/> </property> <property name = "targetmethod"> <value> execute </value> </property> </bean>
MethotingJobdetailFactoryBeanクラスのMethodInvokingメソッドは、シリアル化をサポートしていないため、Quartzのタスクをデータベースにシリアル化するとエラーが発生します。
まず、MetholokingJobdetailFactoryBeanの問題を解決します。 Springソースコードを変更せずに、このクラスの使用を避け、JobDetailを直接呼び出すことができます。ただし、JobDetailの実装を使用するには、自分でMothodinvokingのロジックを実装する必要があります。 JobDetailのJobClassおよびJobDataaSmapプロパティを使用して、工場(マネージャー)をカスタマイズして同じ目的を達成できます。たとえば、この例では、この機能を実装するために新しいMydetailquartzjobbeanが作成されます。
3.2 mydetailquartzjobbean.javaファイル
Package org.lxh.mvc.jobbean; Import java.lang.refllect.method; Import org.apache.commons.logging.log; Import org.apache.commons.logging.logfactory; Import org.quartz.jobexecutioncontext; Importz.jobexecuteexcection; org.springframework.context.applicationcontext; Import org.springframework.scheduling.quartzjobbean; public class mydetailquartzjobbeanがQuartzjobbeanを拡張します{保護された最終logger = logfactory.getlog(getclass();プライベート文字列ターゲットオブジェクト。プライベートストリングターゲットメトッド; Private ApplicationContext CTX;保護されたvoid executeinternal(jobexecutioncontextコンテキスト)throws jobexecutionexception {try {logger.info( "execute [" + targetObject + "] and ain >>>>"); Object oargetobject = ctx.getBean(TargetObject);方法m = null; try {m = oargetobject.getClass()。getMethod(targetMethod、new class [] {}); M.Invoke(OargetObject、new Object [] {}); } catch(securityException e){logger.error(e); } catch(nosuchmethodexception e){logger.error(e); }} catch(Exception E){新しいJobeExeCutionException(e); }} public void setApplicationContext(applicationContext ApplicationContext){this.ctx = applicationContext; } public void settargetObject(string targetobject){this.targetObject =ターゲットオーブジェクト; } public void settargetMethod(String TargetMethod){this.targetMethod = targetMethod; }}3.3実際のジョブ実装クラス
テストクラスでは、システムの現在の時刻を印刷する機能が単純に実装されます。
Package org.lxh.mvc.job; Import java.io.serializable; Import java.util.date; import org.apache.commons.logging.log; import org.apache.commons.logging.logactory; logfactory; public class test emplements {private logfactory = logactory.getery.(test.class); private static final long serialversionuid = -2073310586499744415l; public void execute(){date date = new date(); System.out.println(date.tolocalestring()); }}3.4 Quartz.xmlファイルを構成します
<bean id = "test" scope = "prototype"> </bean> <bean id = "testjobtask"> <property name = "jobclass"> <balue> org.lxh.mvc.jobbean.mydetailquartzjobbean </value> </property> <propertion = "ext" "> < key = "TargetMethod" value = "execute"/> </map> </property> </bean> <bean name = "testtrigger"> <property name = "jobdetail" ref = "testjobtask"/> <プロパティname = "cronexpression" value = "0/1 * * * *?" /> </bean> <bean id = "quartzscheduler"> <プロパティ名= "configlocation" value = "classpath:quartz.properties"/> <プロパティ名= "トリガー"> <ref> <ref bean = "testtrigger"/> </list> </property> <propertion
3.5テスト
serveraとserverbのコードと構成はまったく同じです。最初にserveraを起動してから、serverbを開始します。サーバーがシャットダウンされた後、ServerBはシャットダウンを監視し、Serveraで実行されているジョブを引き継ぎ、実行を継続します。
4。Quartzクラスターインスタンス(シングルクォーツ)
Spring+Quartzのクラスター構成を実装していますが、SpringとQuartzの間の互換性の問題のため、この方法を使用することはまだお勧めしません。このセクションでは、Quartzで個別に構成されたクラスターを実装しました。これは、Spring+Quartzと比較してシンプルで安定しています。
4.1エンジニアリング構造
Quartzのみを使用して、図3.1に示すように、クラスター関数、コード構造、および必要なサードパーティのJARパッケージを実装します。その中で、MySQLバージョン:5.1.52、およびMySQLドライバーバージョン:MySQL-Connector-Java-5.1.5-bin.jar(5.1.52の場合、MySQLドライバーと組み合わせると正常に実行できないクォーツにこのバグが使用できないため、このドライバーのバグを使用することをお勧めします)。
図4.1石英クラスターエンジニアリング構造と必要なサードパーティの瓶パッケージ
その中で、Quartz.PropertiesはQuartz構成ファイルであり、SRCディレクトリに配置されています。そのようなファイルがない場合、QuartzはJARパッケージにQuartz.Propertiesファイルを自動的にロードします。 Simplerecoveryjob.javaとsimplerecoverystatefulfuljob.javaは2つのジョブです。 clusterexample.javaは、スケジューリング情報、メカニズムのトリガー、対応するテストの主な関数を書き込みます。
4.2構成ファイルQuartz.Properties
デフォルトのファイル名Quartz.Propertiesは、「org.quartz.jobstore.iscrustered」プロパティを「true」に設定することにより、クラスター機能をアクティブにするために使用されます。クラスター内の各インスタンスには、一意の「インスタンスID」(「org.quartz.scheduler.instanceId」プロパティが必要ですが、同じ「スケジューラインスタンス名」( "org.quartz.scheduler.instancename")が必要です。以下の例外を除き、構成ファイルの内容は同じでなければなりません。
a。スレッドプールサイズ。
b。異なる「org.quartz.scheduler.instanceId」属性値(「auto」に設定)。
#========================================================================== ============================================================================== ============================================================================== ============================================================================== ============================================================================== ============================================================================== ============================================================================== ============================================================================== Auto#========================================================================================= =========================================================================================== org.quartz.jobstore.impl.jdbcjobstore.jobstoretxorg.quartz.jobstore.driverdelegateclass = org.quartz.impl.jobstore.stdjdbcdelegateorg.quartz.jobstore.tableprefix = qrtz_org.quartz.jobstore.isclustered = trueorg.quartz.jobstore.clustercheckininterval = 10000 org.quartz.jobstore.datasource = myds #=========================================================================================== ============================================================================================ ============================================================================================ ============================================================================================ #========================================================================================================================================================================================================================== = com.mysql.jdbc.driverorg.quartz.datasource.myds.url = jdbc:mysql://192.168.31.18:3306/test?useunicode = true&charatereCoding = utf-8org.quartz.datasource.myds.suser = rootorg.quartz.datasource.myds.password = 123456org.quartz.datasource.myds.maxconnections = 30#============================================================================ ========================================================================== ========================================================================== ========================================================================== ========================================================================== ========================================================================== ========================================================================== ========================================================================== org.quartz.simpl.simplethreadpoolorg.quartz.threadpool.threadcount = 5org.quartz.threadpool.threadpriority = 5org.quartz.threadpool.threadsinheritcontextclassloasherofizealizingthread = true
4.3 ClusterExample.javaファイル
パッケージクラスター; java.util.dateのインポート;インポートorg.quartz.jobdetail; Import org.quartz.scheduler; import org.quartz.schedulerfactory; Import org.quartz.simpletrigger; Import org.quartz.impl.stdschedulefactory(Public Class clurterexample); Sthrows Exception {System.out.println( "****既存のジョブ/トリガー*****"); // unschedule jobs string [] groups = inscheduler.getTriggerGroupNames(); for(int i = 0; i <groups.length; i ++){string [] names = inscheduler.gettriggernames(groups [i]); for(int j = 0; j <names.length; j ++){inscheduler.unschedulejob(names [j]、groups [i]); }} //ジョブグループ= inscheduler.getjobgroupNames(); for(int i = 0; i <groups.length; i ++){string [] names = inscheduler.getJobnames(groups [i]); for(int j = 0; j <names.length; j ++){inscheduler.deletejob(names [j]、groups [i]); }}} public void run(boolean implearjobs、boolean inschedulejobs)スロー例外{//最初にスケジューラSchedulerFactory SF = new stdschedulerfactory()への参照を取得する必要があります。スケジューラスケジュール= sf.getScheduler(); if(infirearjobs){cleanup(sched); } system.out.println( "----------------------------"); if(inschedulejobs){ System.out.println("------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- //スケジュールが下がったときにこのジョブを再実行します。SetRequestsRecovery(True);実行: " + trigger.getNextFireTime() +"および繰り返します: " + trigger.getRepeatCount() +" + " + trigger.getRepeatInterval() / 1000 +"秒 "); schedulejob(job、trigger); count ++; job = new jobstail(" + count、count、count、count ++; //スケジュールが進行中である場合、スケジュールを再実行します。 +「 + trigger.getNextFiretime() + " +" + trigger.getRepeatCount() + " + trigger.getRepeatInterval() / 1000 +"秒 ")。 system.out.println( "---------------------------------"); System.out.println("-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Thread.sleep(3600L * 1000l);} catch(例外e){} System.out.println("--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- args.length(args [i] .equalsignecase = true; }}4.4 Simplerecoveryjob.java
パッケージクラスター; java.io.serializable; import java.util.date; import org.apache.commons.logging.log; import org.apache.commons.logging.logactory; import org.quartz.job; Import org.quartz.jobexicutioncectodext;繰り返し実行し、関連するメソッドに関連するコードを記述したいということです。前提は次のとおりです。SimpleJobクラスがインスタンス化されている場合と実行メソッドが呼び出されたときにジョブインターフェイス//を実装してください。 public simplerecoveryjob(){} public void execute(jobexecutioncontext context)throws jobexecutionexception {//このジョブは単にジョブ名とジョブがjobname = context.getjobdetail()。 System.out.println( "Job 11111111111111111 SimplerecoveryJobは次のように述べています。 }}4.5操作結果
サーバーAとサーバーBの構成とコードはまったく同じです。実行方法:clusterexample.javaを任意のホストで実行し、スケジュールにタスクを追加し、実行の結果を観察します。
serveraを実行すると、結果を図4.2に示します。
図4.2サーバー操作結果1
ServerBをオンにした後、ServeraとServerBの出力を図4.3および4.4に示します。
図4.3 Servera実行結果2
図4.4 ServerB操作結果1
図4.3および4.4から、ServerBがオンになった後、システムがバランスの責任を自動的に実現し、ServerBがJob1を引き継ぐことがわかります。 Serveraをシャットダウンした後、ServerBの実行結果を図4.5に示します。
図4.5 ServerB操作結果2
図4.5からわかるように、ServerBはServeraが失われ、タスクJob2を引き継ぎ、この例外時間中に実行する必要があるJob2にServeraを失うことを検出できます。
5。注意すべきこと
5.1時間同期の問題
クォーツは、実際には、同じマシンまたは異なるマシンでノードを実行するかどうかは気にしません。クラスターを異なるマシンに配置すると、水平クラスターと呼ばれます。ノードが同じマシンで実行されると、垂直クラスターと呼ばれます。垂直クラスターの場合、単一の障害点の問題があります。これは、マシンがクラッシュするとすべてのノードが終了するため、高可用性アプリケーションには受け入れられません。水平クラスターの場合、時間同期の問題があります。
ノードはタイムスタンプを使用して、独自の最後のチェックイン時間を他のインスタンスに通知します。ノードのクロックが将来の時間に設定されている場合、ランニングスケジューラはノードが低下していることを認識しなくなります。一方、ノードのクロックが過去の時間に設定されている場合、おそらく他のノードは、ノードが落ちたことを判断し、仕事を引き受けて再実行しようとします。コンピュータークロックを同期する最も簡単な方法は、インターネットタイムサーバーを使用することです。
5.2ジョブのために競合するノードの問題
Quartzはランダムな負荷分散アルゴリズムを使用するため、ジョブはさまざまなインスタンスによってランダムな方法で実行されます。 Quartzの公式ウェブサイトは、現在、クラスター内の特定のノードにジョブを割り当てる(PIN)する方法はないと述べました。
5.3クラスターからジョブリストを取得するための問題
現在、データベースクエリを直接入力しない場合、クラスター内のすべての実行ジョブのリストを取得する簡単な方法はありません。スケジューラインスタンスを要求すると、そのインスタンスで実行されているジョブのリストのみが取得されます。 Quartzの公式Webサイトでは、データベースにアクセスするためにJDBCコードを書いて、対応するテーブルからすべてのジョブ情報を取得できることを推奨しています。
要約します
上記は、この記事のコンテンツ全体です。この記事の内容には、すべての人の研究や仕事に特定の参照値があることを願っています。ご質問がある場合は、メッセージを残してコミュニケーションをとることができます。 wulin.comへのご支援ありがとうございます。