ステップフローの制御
所有するジョブ内でステップをグループ化できるため、あるステップから別のステップへのジョブの「流れ」を制御できる必要があります。Step
の障害は、必ずしも Job
の障害を意味するわけではありません。さらに、どの Step
を次に実行するかを決定する「成功」の型が複数存在する場合があります。Steps
のグループの構成方法によっては、特定のステップがまったく処理されない場合もあります。
フロー定義でのステップ Bean メソッドプロキシ ステップインスタンスはフロー定義内で一意である必要があります。フロー定義でステップに複数の結果がある場合、ステップの同じインスタンスがフロー定義メソッド ( 次の例では、ステップがパラメーターとしてフローまたはジョブの Bean 定義メソッドに挿入されます。この依存性注入スタイルにより、フロー定義内のステップの一意性が保証されます。ただし、 Spring Framework での Bean メソッドプロキシの詳細については、"@Configuration アノテーションの使用" セクションを参照してください。 |
シーケンシャルフロー
最も単純なフローシナリオは、次の図に示すように、すべてのステップが順番に実行されるジョブです。
これは、step
で next
を使用することで実現できます。
Java
XML
次の例は、Java で next()
メソッドを使用する方法を示しています。
@Bean
public Job job(JobRepository jobRepository, Step stepA, Step stepB, Step stepC) {
return new JobBuilder("job", jobRepository)
.start(stepA)
.next(stepB)
.next(stepC)
.build();
}
次の例は、XML で next
属性を使用する方法を示しています。
<job id="job">
<step id="stepA" parent="s1" next="stepB" />
<step id="stepB" parent="s2" next="stepC"/>
<step id="stepC" parent="s3" />
</job>
上記のシナリオでは、最初にリストされた Step
であるため、stepA
が最初に実行されます。stepA
が正常に完了すると、stepB
が実行されます。ただし、step A
が失敗すると、Job
全体が失敗し、stepB
は実行されません。
Spring Batch XML 名前空間では、構成にリストされている最初のステップは、常に Job によって実行される最初のステップです。他のステップ要素の順序は重要ではありませんが、最初のステップは常に XML で最初に表示される必要があります。 |
条件付きフロー
前の例では、次の 2 つの可能性しかありません。
step
が成功し、次のstep
を実行する必要があります。step
が失敗したため、job
も失敗するはずです。
多くの場合、これで十分です。しかし、step
の障害が障害を引き起こすのではなく、異なる step
をトリガーするシナリオについてはどうでしょうか? 次の図は、このようなフローを示しています。
Java
XML
Java API は、フローと、ステップが失敗した場合の処理を指定できる流れるような一連のメソッドを提供します。次の例は、1 つのステップ (stepA
) を指定してから、stepA
が成功したかどうかに応じて、2 つの異なるステップ (stepB
または stepC
) のいずれかに進む方法を示しています。
@Bean
public Job job(JobRepository jobRepository, Step stepA, Step stepB, Step stepC) {
return new JobBuilder("job", jobRepository)
.start(stepA)
.on("*").to(stepB)
.from(stepA).on("FAILED").to(stepC)
.end()
.build();
}
より複雑なシナリオを処理するために、Spring Batch XML 名前空間を使用すると、step 要素内に transitions 要素を定義できます。そのような遷移の 1 つが next
要素です。next
属性と同様に、next
要素は、次に実行する Step
を Job
に通知します。ただし、属性とは異なり、特定の Step
で任意の数の next
要素を使用でき、失敗した場合のデフォルトの動作はありません。これは、遷移要素が使用されている場合、Step
遷移のすべての動作を明示的に定義する必要があることを意味します。また、1 つのステップに next
属性と transition
要素の両方を含めることはできないことに注意してください。
次の例に示すように、next
要素は、照合するパターンと次に実行するステップを指定します。
<job id="job">
<step id="stepA" parent="s1">
<next on="*" to="stepB" />
<next on="FAILED" to="stepC" />
</step>
<step id="stepB" parent="s2" next="stepC" />
<step id="stepC" parent="s3" />
</job>
Java
XML
java 構成を使用する場合、on()
メソッドは、単純なパターンマッチングスキームを使用して、Step
の実行結果である ExitStatus
と一致します。
XML 構成を使用する場合、遷移要素の on
属性は、単純なパターンマッチングスキームを使用して、Step
の実行から生じる ExitStatus
を照合します。
パターンでは 2 つの特殊文字のみが許可されます。
*
はゼロ個以上の文字と一致します?
は正確に 1 文字に一致します
例: c*t
は cat
および count
と一致し、c?t
は cat
と一致しますが、count
とは一致しません。
Step
の遷移要素の数に制限はありませんが、要素でカバーされていない ExitStatus
が Step
実行の結果である場合、フレームワークは例外をスローし、Job
は失敗します。フレームワークは、最も具体的なものから最も具体的でないものへと自動的に遷移を並べ替えます。これは、前の例で順序が stepA
に置き換えられたとしても、FAILED
の ExitStatus
は依然として stepC
に移動することを意味します。
バッチステータスと終了ステータス
条件付きフロー用に Job
を構成する場合、BatchStatus
と ExitStatus
の違いを理解することが重要です。BatchStatus
は、JobExecution
と StepExecution
の両方のプロパティである列挙であり、Job
または Step
のステータスを記録するためにフレームワークによって使用されます。次の値のいずれかです: COMPLETED
、STARTING
、STARTED
、STOPPING
、STOPPED
、FAILED
、ABANDONED
または UNKNOWN
。それらのほとんどは自明です。COMPLETED
は、ステップまたはジョブが正常に完了したときに設定されるステータスであり、FAILED
は失敗したときに設定される、などです。
Java
XML
次の例には、Java 構成を使用する場合の on
要素が含まれています。
...
.from(stepA).on("FAILED").to(stepB)
...
次の例には、XML 構成を使用する場合の next
要素が含まれています。
<next on="FAILED" to="stepB" />
一見すると、on
は、それが属する Step
の BatchStatus
を参照しているように見えます。ただし、実際には Step
の ExitStatus
を参照しています。名前が示すように、ExitStatus
は実行終了後の Step
のステータスを表します。
Java
XML
Java 構成を使用する場合、前述の Java 構成の例に示されている on()
メソッドは ExitStatus
の終了コードを参照します。
より具体的には、XML 構成を使用する場合、前述の XML 構成の例に示されている next
要素は、ExitStatus
の終了コードを参照します。
英語で言うと: 「終了コードが FAILED の場合は stepB に進む」。デフォルトでは、終了コードは常に Step
の BatchStatus
と同じであるため、前のエントリが機能します。ただし、終了コードを変更する必要がある場合はどうすればよいでしょうか ? サンプルプロジェクト内のスキップサンプルジョブが良い例です。
Java
XML
次の例は、Java で別の終了コードを操作する方法を示しています。
@Bean
public Job job(JobRepository jobRepository, Step step1, Step step2, Step errorPrint1) {
return new JobBuilder("job", jobRepository)
.start(step1).on("FAILED").end()
.from(step1).on("COMPLETED WITH SKIPS").to(errorPrint1)
.from(step1).on("*").to(step2)
.end()
.build();
}
次の例は、XML で別の終了コードを操作する方法を示しています。
<step id="step1" parent="s1">
<end on="FAILED" />
<next on="COMPLETED WITH SKIPS" to="errorPrint1" />
<next on="*" to="step2" />
</step>
step1
には 3 つの可能性があります。
Step
が失敗しました。その場合、ジョブは失敗するはずです。Step
は正常に完了しました。Step
は正常に完了しましたが、終了コードはCOMPLETED WITH SKIPS
です。この場合、別のステップを実行してエラーを処理する必要があります。
上記の構成は機能します。ただし、次の例に示すように、レコードをスキップした実行の条件に基づいて終了コードを変更する必要があります。
public class SkipCheckingListener implements StepExecutionListener {
@Override
public ExitStatus afterStep(StepExecution stepExecution) {
String exitCode = stepExecution.getExitStatus().getExitCode();
if (!exitCode.equals(ExitStatus.FAILED.getExitCode()) &&
stepExecution.getSkipCount() > 0) {
return new ExitStatus("COMPLETED WITH SKIPS");
} else {
return null;
}
}
}
上記のコードは StepExecutionListener
であり、最初に Step
が成功したことを確認し、次に StepExecution
のスキップカウントが 0 より大きいかどうかを確認します。両方の条件が満たされた場合、終了コード COMPLETED WITH SKIPS
を持つ新しい ExitStatus
が返されます。
停止の構成
BatchStatus
および ExitStatus
の説明の後、Job
に対して BatchStatus
と ExitStatus
がどのように決定されるのか疑問に思うかもしれません。これらのステータスは Step
では実行されるコードによって決定されますが、Job
のステータスは構成に基づいて決定されます。
これまでのところ、説明したすべてのジョブ構成には、遷移のない最終的な Step
が少なくとも 1 つあります。
Java
XML
次の Java の例では、step
が実行された後、Job
が終了します。
@Bean
public Job job(JobRepository jobRepository, Step step1) {
return new JobBuilder("job", jobRepository)
.start(step1)
.build();
}
次の XML の例では、step
が実行された後、Job
が終了します。
<step id="step1" parent="s3"/>
Step
に遷移が定義されていない場合、Job
のステータスは次のように定義されます。
Step
がFAILED
のExitStatus
で終わる場合、Job
のBatchStatus
とExitStatus
は両方ともFAILED
です。それ以外の場合、
Job
のBatchStatus
とExitStatus
は両方ともCOMPLETED
です。
バッチジョブを終了するこの方法は、単純な順次ステップジョブなどの一部のバッチジョブには十分ですが、カスタム定義のジョブ停止シナリオが必要になる場合があります。この目的のために、Spring Batch は、(前に説明した next
要素に加えて) Job
を停止するための 3 つの遷移要素を提供します。これらの停止要素はそれぞれ、特定の BatchStatus
で Job
を停止します。停止遷移要素は、Job
の Steps
の BatchStatus
または ExitStatus
のいずれにも影響しないことに注意することが重要です。これらの要素は、Job
の最終ステータスのみに影響します。例: ジョブ内のすべてのステップのステータスが FAILED
であるが、ジョブのステータスが COMPLETED
である可能性があります。
ステップで終了
ステップ終了を構成すると、Job
が COMPLETED
の BatchStatus
で停止するように指示されます。COMPLETED
のステータスで終了した Job
は再開できません (フレームワークは JobInstanceAlreadyCompleteException
をスローします)。
Java
XML
Java 構成を使用する場合、このタスクには end
メソッドが使用されます。end
メソッドでは、Job
の ExitStatus
をカスタマイズするために使用できるオプションの exitStatus
パラメーターも使用できます。exitStatus
値が指定されていない場合、ExitStatus
はデフォルトで COMPLETED
になり、BatchStatus
と一致します。
XML 構成を使用する場合、このタスクに end
エレメントを使用できます。end
要素では、Job
の ExitStatus
をカスタマイズするために使用できるオプションの exit-code
属性も使用できます。exit-code
属性が指定されていない場合、ExitStatus
はデフォルトで COMPLETED
になり、BatchStatus
と一致します。
次のシナリオを検討してください: step2
が失敗すると、Job
は COMPLETED
の BatchStatus
と COMPLETED
の ExitStatus
で停止し、step3
は実行されません。それ以外の場合、実行は step3
に移動します。step2
に障害が発生した場合、Job
は再起動できないことに注意してください (ステータスが COMPLETED
であるため)。
Java
XML
次の例は、Java でのシナリオを示しています。
@Bean
public Job job(JobRepository jobRepository, Step step1, Step step2, Step step3) {
return new JobBuilder("job", jobRepository)
.start(step1)
.next(step2)
.on("FAILED").end()
.from(step2).on("*").to(step3)
.end()
.build();
}
次の例は、XML のシナリオを示しています。
<step id="step1" parent="s1" next="step2">
<step id="step2" parent="s2">
<end on="FAILED"/>
<next on="*" to="step3"/>
</step>
<step id="step3" parent="s3">
ステップの失敗
特定のポイントで失敗するようにステップを構成すると、Job
が FAILED
の BatchStatus
で停止するように指示されます。終了とは異なり、Job
の障害は Job
の再起動を妨げません。
XML 構成を使用する場合、fail
要素は、Job
の ExitStatus
をカスタマイズするために使用できるオプションの exit-code
属性も許可します。exit-code
属性が指定されていない場合、BatchStatus
と一致するように、ExitStatus
はデフォルトで FAILED
になります。
次のシナリオを検討してください: step2
が失敗すると、Job
は FAILED
の BatchStatus
と EARLY TERMINATION
の ExitStatus
で停止し、step3
は実行されません。それ以外の場合、実行は step3
に移動します。さらに、step2
が失敗し、Job
が再起動された場合、実行は step2
で再び開始されます。
Java
XML
次の例は、Java でのシナリオを示しています。
@Bean
public Job job(JobRepository jobRepository, Step step1, Step step2, Step step3) {
return new JobBuilder("job", jobRepository)
.start(step1)
.next(step2).on("FAILED").fail()
.from(step2).on("*").to(step3)
.end()
.build();
}
次の例は、XML のシナリオを示しています。
<step id="step1" parent="s1" next="step2">
<step id="step2" parent="s2">
<fail on="FAILED" exit-code="EARLY TERMINATION"/>
<next on="*" to="step3"/>
</step>
<step id="step3" parent="s3">
特定のステップでジョブを停止する
特定のステップで停止するようにジョブを構成すると、Job
は STOPPED
の BatchStatus
で停止するように指示されます。Job
を停止すると、処理が一時的に中断する可能性があるため、オペレーターは Job
を再始動する前に何らかのアクションを取ることができます。
Java
XML
Java 構成を使用する場合、stopAndRestart
メソッドには restart
属性が必要です。この属性は、ジョブの再開時に実行を開始するステップを指定します。
XML 構成を使用する場合、stop
要素には、Job
が再開されたときに実行を開始するステップを指定する restart
属性が必要です。
次のシナリオを検討してください: step1
が COMPLETE
で終了すると、ジョブは停止します。再起動すると、step2
で実行が開始されます。
Java
XML
次の例は、Java でのシナリオを示しています。
@Bean
public Job job(JobRepository jobRepository, Step step1, Step step2) {
return new JobBuilder("job", jobRepository)
.start(step1).on("COMPLETED").stopAndRestart(step2)
.end()
.build();
}
次のリストは、XML でのシナリオを示しています。
<step id="step1" parent="s1">
<stop on="COMPLETED" restart="step2"/>
</step>
<step id="step2" parent="s2"/>
プログラムによるフローの決定
場合によっては、次に実行するステップを決定するために、ExitStatus
よりも多くの情報が必要になることがあります。この場合、次の例に示すように、JobExecutionDecider
を使用して決定を支援できます。
public class MyDecider implements JobExecutionDecider {
public FlowExecutionStatus decide(JobExecution jobExecution, StepExecution stepExecution) {
String status;
if (someCondition()) {
status = "FAILED";
}
else {
status = "COMPLETED";
}
return new FlowExecutionStatus(status);
}
}
Java
XML
次の例では、Java 構成を使用するときに、JobExecutionDecider
を実装する Bean が next
呼び出しに直接渡されます。
@Bean
public Job job(JobRepository jobRepository, MyDecider decider, Step step1, Step step2, Step step3) {
return new JobBuilder("job", jobRepository)
.start(step1)
.next(decider).on("FAILED").to(step2)
.from(decider).on("COMPLETED").to(step3)
.end()
.build();
}
次のサンプルジョブ構成では、decision
は、使用する決定者とすべての遷移を指定します。
<job id="job">
<step id="step1" parent="s1" next="decision" />
<decision id="decision" decider="decider">
<next on="FAILED" to="step2" />
<next on="COMPLETED" to="step3" />
</decision>
<step id="step2" parent="s2" next="step3"/>
<step id="step3" parent="s3" />
</job>
<beans:bean id="decider" class="com.MyDecider"/>
フローの分割
これまでに説明したすべてのシナリオには、Job
が関与しており、その手順は一度に 1 つずつ線形に実行されます。この典型的なスタイルに加えて、Spring Batch では、並列フローでジョブを構成することもできます。
Java
XML
Java ベースの構成により、提供されたビルダーを介して分割を構成できます。次の例に示すように、split
要素には 1 つ以上の flow
要素が含まれており、個別のフロー全体を定義できます。split
要素には、next
属性や next
、end
、または fail
要素など、前述の移行要素を含めることもできます。
@Bean
public Flow flow1(Step step1, Step step2) {
return new FlowBuilder<SimpleFlow>("flow1")
.start(step1)
.next(step2)
.build();
}
@Bean
public Flow flow2(Step step3) {
return new FlowBuilder<SimpleFlow>("flow2")
.start(step3)
.build();
}
@Bean
public Job job(JobRepository jobRepository, Flow flow1, Flow flow2, Step step4) {
return new JobBuilder("job", jobRepository)
.start(flow1)
.split(new SimpleAsyncTaskExecutor())
.add(flow2)
.next(step4)
.end()
.build();
}
XML 名前空間では、split
要素を使用できます。次の例に示すように、split
要素には 1 つ以上の flow
要素が含まれており、個別のフロー全体を定義できます。split
要素には、next
属性や next
、end
、または fail
要素など、前述の移行要素を含めることもできます。
<split id="split1" next="step4">
<flow>
<step id="step1" parent="s1" next="step2"/>
<step id="step2" parent="s2"/>
</flow>
<flow>
<step id="step3" parent="s3"/>
</flow>
</split>
<step id="step4" parent="s4"/>
フロー定義とジョブ間の依存関係の外部化
ジョブ内のフローの一部は、個別の Bean 定義として外部化してから再利用できます。これには 2 つの方法があります。1 つ目は、他の場所で定義されたフローへの参照としてフローを宣言することです。
Java
XML
次の Java の例は、別の場所で定義されたフローへの参照としてフローを宣言する方法を示しています。
@Bean
public Job job(JobRepository jobRepository, Flow flow1, Step step3) {
return new JobBuilder("job", jobRepository)
.start(flow1)
.next(step3)
.end()
.build();
}
@Bean
public Flow flow1(Step step1, Step step2) {
return new FlowBuilder<SimpleFlow>("flow1")
.start(step1)
.next(step2)
.build();
}
次の XML の例は、他の場所で定義されたフローへの参照としてフローを宣言する方法を示しています。
<job id="job">
<flow id="job1.flow1" parent="flow1" next="step3"/>
<step id="step3" parent="s3"/>
</job>
<flow id="flow1">
<step id="step1" parent="s1" next="step2"/>
<step id="step2" parent="s2"/>
</flow>
前の例に示すように、外部フローを定義すると、インラインで宣言されているかのように、外部フローからのステップがジョブに挿入されます。このようにして、多くのジョブが同じテンプレートフローを参照し、そのようなテンプレートを異なる論理フローに構成できます。これは、個々のフローの統合テストを分離する良い方法でもあります。
外部化されたフローの他の形式は、JobStep
を使用することです。JobStep
は FlowStep
に似ていますが、実際には、指定されたフロー内のステップに対して個別のジョブ実行を作成して起動します。
Java
XML
次の例は、Java での JobStep
の例を示しています。
@Bean
public Job jobStepJob(JobRepository jobRepository, Step jobStepJobStep1) {
return new JobBuilder("jobStepJob", jobRepository)
.start(jobStepJobStep1)
.build();
}
@Bean
public Step jobStepJobStep1(JobRepository jobRepository, JobLauncher jobLauncher, Job job, JobParametersExtractor jobParametersExtractor) {
return new StepBuilder("jobStepJobStep1", jobRepository)
.job(job)
.launcher(jobLauncher)
.parametersExtractor(jobParametersExtractor)
.build();
}
@Bean
public Job job(JobRepository jobRepository) {
return new JobBuilder("job", jobRepository)
// ...
.build();
}
@Bean
public DefaultJobParametersExtractor jobParametersExtractor() {
DefaultJobParametersExtractor extractor = new DefaultJobParametersExtractor();
extractor.setKeys(new String[]{"input.file"});
return extractor;
}
次の例では、XML での JobStep
の例を示しています。
<job id="jobStepJob" restartable="true">
<step id="jobStepJob.step1">
<job ref="job" job-launcher="jobLauncher"
job-parameters-extractor="jobParametersExtractor"/>
</step>
</job>
<job id="job" restartable="true">...</job>
<bean id="jobParametersExtractor" class="org.spr...DefaultJobParametersExtractor">
<property name="keys" value="input.file"/>
</bean>
ジョブパラメーターエクストラクターは、実行される Job
の JobParameters
に Step
の ExecutionContext
を変換する方法を決定する戦略です。JobStep
は、ジョブとステップを監視およびレポートするためのより詳細なオプションが必要な場合に役立ちます。JobStep
を使用すると、多くの場合、「ジョブ間の依存関係を作成するにはどうすればよいですか ? 」という質問に対する良い答えになります。これは、大規模なシステムを小さなモジュールに分割し、ジョブの流れを制御するための優れた方法です。