Upload
norito-agetsuma
View
2.566
Download
4
Embed Size (px)
Citation preview
jBatch実践入門 NTTコムウェア株式会社 上妻 宜人 (あげつま のりと)
はてなブログ : 見習いプログラミング日記
【Java Day Tokyo 2015 6-5】
上妻 宜人 あげつま のりと
• Javaトラブル解析/技術サポート業務に従事
• Java EE について調べて伝えることが好き
• 日本Javaユーザグループでの講演
• JJUG CCC 2014, ナイトセミナ, JavaOne報告会 など
『バッチジョブ』
どのように実装していますか?
よくあるバッチアプリ構成を
思い浮かべてみます。
よくあるバッチ構成例
ジョブスケジューラ
ジョブネットA Job1 Job2-1
Job2-2
Job3
マシンA
Job1 Job2 Job2 Job3
mainメソッドから始まるJavaプログラム
マシンB
ジョブスケジューラ
ジョブネットA Job1 Job2-1
Job2-2
Job3
マシンA
Job1 Job2 Job2 Job3
mainメソッドから始まるJavaプログラム
マシンB
• 試験期間の後半に問題が見つかる
• 一度に大量ロードしてOutOfMemoryError
• JDBCのexecuteBatch未使用による性能遅延
• 似たようなジョブでも構造がばらばら
• 独自フレームワークは難読化と隣り合わせ
よくある困ったジョブ実装
ジョブ実装への様々な要求
• 耐エラー性
• 不正データが混在していても弾いて準正常終了
• リスタート可能
• エラーによる再実行時には途中から再開可
• 多様な入出力形式
• CSVファイルやRDBMSが入力とは限らない
• Web API によりjson形式の入力を持ってくる 等
これら課題を解決するための、
1つの候補が Java EE7 より導入 (2013/5 release)
JSR 352 Batch Application for
The Java Platform
本日のコンテンツ
• jBatchとは何か
• jBatchをどうやって使うのか
• メリット と デメリット
• よくある疑問
• まとめ
jBatchとは何か?
• Java EE7 より導入されたバッチフレームワーク
• 各ジョブの実装を楽にするのが目的
• Spring Batch の構造を引き継いで仕様化
jBatchの全体像
1. バッチ用語とアーキテクチャの定義
JobOperator Job
Item Processor
Item Writer
2. ジョブXML 3. ユーザが実装するAPI <job id=“monthly_billing”> <step id=“step1” next=“step2”> <step id=“step2”> </job>
Step1 Step2 Step3
Job Repository
Item Reader
public interface ItemReader { Object readItem() throws ... }
jBatch機能 #1 順序制御
ジョブスケジューラ
Job1 job2
Cron or EJBタイマ
0 4 * * * batch.sh
jBatch Job
Step1 Step2 Step4
Decision (条件分岐)
Step3-1
Step3-2 Split (並行実行)
Job XML
</>
ジョブフローはXML、ステップや分岐条件はJavaで実装
Java
Java Java
Java
Java
jBatch機能#2 処理のフレームワーク化
<<interface>> Item
Processor
<<interface>> Item
Writer
Step1
<<interface>> Item
Reader
• バッチ処理の多くは 『入力⇒処理⇒出力』 の流れ
• 入出力と処理を分けて作成し、jBatchが呼び出す構造
• フレームワークによる設計の再利用
jBatch Runtime
readItem()
jBatch Job
Java Java Java
processItem() writeItems()
Step2
jBatch機能#3 チェックポイント
• 異常終了に備えてチェックポイント情報を収集
• デフォルト 10アイテム処理間隔
• リスタート時にItemReaderとItemWriterに渡す
• 例えば、ファイルの途中から再処理が可能
Job Repository
10
30 ジョブ 完了
10行完了
COMIT
20 COMIT
× 20行完了
restart
20行目から再処理
start
jBatch機能#4 エラーハンドリング
• スキップ
• 例外が投げられたレコードの処理をスキップ
• 例: 入力チェックエラー行はロギングのちスキップ
• 上限を設定して、ジョブ自体の異常終了も可能
• リトライ
• 例外が投げられたら該当のステップを自動リトライ
• デフォルトはリトライなし。ジョブ異常終了。
jBatchのアーキテクチャ
JobOperator Job
Item Processor
Item Writer
Step1 Step2 Step3
Job Repository
Item Reader
• JobOperatorからジョブ起動
• 1つのジョブは複数のステップより構成
• 各ステップは Read – Process – Write に分けて実装
• 起動終了時刻、終了ステータスをJobRepositoryに保存
ジョブ
請求書発行ジョブ
【Step1】 請求額確定
【Step2】 PDF生成
【Step3】 メール送付
• トップレベル要素
• jBatchにジョブネットやジョブグループのような考え方はない
• ジョブは必ず1つ以上のステップを含む
• パラメータ、再実行可否 を設定可能
• param : /var/file/売り上げデータ.csv
• エラー時は途中ステップから再実行可
(例: メールサーバ異常時には、Step3から再実行)
(デフォルト再実行可)
ステップ
• ユーザがJavaで実装する処理
• ステップ毎に2種類の実装方式が選べる
• チャンク: Reader, Processor, Writer を分けて実装
• バッチレット: 単純に1回だけ呼ばれるタスク
ステップ: チャンク方式
• ItemReader、ItemProcessor、ItemWriter の3つを実装
• 入力と処理を繰り返した後、一定数まとめて出力
• ファイルやDBなどのレコード単位の処理向け
Item Processor
Item Writer
Step
Item Reader
jBatch Runtime
read
process 『入力 ⇒ 処理』 1レコードずつ 10回繰り返す
Commit ▶
write 10レコード分まとめて書き出す
ステップ: チャンク方式のメリット
• バッチ処理の一定の型を規定する
• 似たような処理を一つの設計にまとめる
• Reader / Writer を汎用ライブラリ化しやすい
Item Processor
Item Writer
Item Reader
• CSVなどのFlatFileReader
• XML / JSON Reader
• JDBC / JPA Reader
• ビジネスロジックの実装
• Readerと同様に ライブラリ提供あり
ステップ: バッチレット方式
• ステップ毎に1回だけ呼び出す方式
• ファイル転送、ファイル圧縮、ジョブ完了メール通知
• Spring Batch では Tasklet と呼ばれている
Step
Batchlet jBatch Runtime
process()
ステップの終了コード
ファイル転送、 ファイル圧縮処理など
ステップ以外のジョブ構成要素 #1
• Flow: ステップをグループ化する
• Decision: 条件分岐
• Split: 並列実行。Flow毎に可能 (Step毎は不可)
ジョブ
Step1-1
Step2-1
Step1-2
Flow2
Flow3
Flow1 Decision
終了
Split
ステップ以外のジョブ構成要素 #2
• Listener: 各ジョブ要素の前後に呼ばれる
• インターセプタのようなイメージ
• 特にスキップ時のSkipListenerはよく使う (ロギング等)
ジョブ Step1
Job Listener
Item Processor
Item Writer
Item Reader
Step Listener
Skip Listener Retry Listener
ここまでのまとめ
• jBatchはバッチフレームワークの標準仕様
• アーキテクチャとジョブXML、APIを規定
• XMLで順序定義し、ステップをAPIに沿って実装
• ジョブがトップレベル。1つ以上のステップを含
む
• ステップは Chunk方式 or Batchlet方式 で実装
• Chunk方式ではReader/Processor/Writerを実装
• Batchlet方式ではBatchletのみ実装
本日のコンテンツ
• jBatchとは何か
• jBatchをどうやって使うのか
• メリット と デメリット
• よくある疑問
• まとめ
jBatch利用の流れ
1. ジョブ設計
• ジョブとステップを抽出する
2. ジョブ実装
• ジョブXMLの作成
• ステップの実装 (Reader/Processor/Writer)
• ジョブの起動
ジョブ設計
• ジョブをステップに分割
• 各ステップの入力と出力を決める
請求書発行ジョブ
【Step1】 請求額確定
【Step2】 請求書生成
【Step3】 メール送付
CSV aa,bb
DBMS
入力 出力 PDF
入力 出力
請求先へ
他システムから 受領した売上データ
請求額TBL 請求書PDF
ジョブ実装 #1 ジョブXMLの作成
<job id=“issueBill”> <!-- Step1 請求額確定 --> <step id=“importInvoice” next=“genPDFBill” > <chunk item-count=“100”> <reader ref=“safesFileReader”/> <processor ref=“billProcessor”/> <writer ref=“billWriter”/> </chunk> </step> <step id=“genPDFBill”> ...
チャンク方式でファイルを読み、DBに書くステップ定義 META-INF/batch-jobs/issueBill.xml
次ステップは請求書生成
読込⇒処理 x 100回後に 出力する(デフォルト10)
ItemReader, ItemProcessor, ItemWriter定義 クラス名の頭を小文字 or FQCN で指定
ジョブ実装 #1-1 ジョブプロパティ定義
<job id=“issueBill”> <!-- Step1 請求額確定 --> <step id=“importInvoice” next=“genPDFBill” > <chunk item-count=“100”>
<reader ref=“safesFileReader”> <properties> <property name=“fixedFileName” value=“input.csv”/> <property name=“fileName” value=“#{jobParameters[‘fileName’]}”/> </properties> </reader> <processor ref=“billProcessor”/> <writer ref=“billWriter”/> </chunk> </step>
...
ItemReaderの入力ファイル名を定義する。 固定の場合と、可変の場合で定義方法は異なる。
固定的なプロパティは 値を直接ジョブXMLに定義する
可変プロパティはプロパティ名を定義 後述のジョブ起動引数で値を指定する
ジョブ実装 #1-2 スキップ定義を追加
<job id=“issueBill”> <!-- Step1 請求額確定 --> <step id=“importInvoice” next=“genPDFBill” >
<chunk item-count=“100” skip-limit=“10”> <reader ref=“safesFileReader”> <properties> <property name=“fixedFileName” value=“input.csv”/> <property name=“fileName” value=“#{jobParameters[‘fileName’]}”/> </properties> </reader> <processor ref=“billProcessor”/> <writer ref=“billWriter”/>
<skippable-exception-class> <include class=“sample.InvalidRecordException”/> </skippable-exception-class> </chunk> <listeners> <listener ref=“logErrorRecordListener” /> <listeners> </step> ...
エラー行をスキップしたい場合は、さらに設定追加
10行までスキップする それ以上はジョブ異常終了
スキップ対象例外 (FQCN)
スキップ時リスナの定義
ジョブ実装 #2 ステップの実装
• Chunkを構成する各クラスを実装する
• スキップリスナを実装する
【Step1】 請求額確定
CSV aa,bb
DBMS
入力 出力
請求額TBL
Bill Processor
SalesFile Reader
Bill Writer
LogErrorRecordListener 【Step2】 請求書生成
【Step3】 メール送付
ジョブ実装 #2-1 ItemReaderの実装
@Named public class SalesFileReader implements ItemReader {
public void open(Serializable checkpoint) { }
public Object readItem() throws Exception{ }
public void close() { }
public Serializable checkpointInfo() { }
}
ItemReaderには4つのAPIが定義されている
ジョブ実装 #2-1-1 ItemReader
@BatchPropertyによるパラメータ取得
@Named public class SalesFileReader implements ItemReader {
@Inject @BatchProperty(name=“fileName”)
private String fileName;
... ジョブXMLのプロパティ名を@BatchProperty引数に指定
<property name=“fileName” value=“#{jobParameters[‘fileName’]}”/>
ジョブ実装 #2-1-2 ItemReader.Open @Named public class SalesFileReader implements ItemReader { ... private BufferedReader reader; private int rowNum;
public void open(Serializable checkpoint) {
reader = Files.newBufferedReader(Paths.get(fileName)); if (checkpoint != null) { // TODO skip reader } }
public Serializable checkpointInfo() {
return rowNum; }
ジョブ実装 #2-1-2 ItemReader.Open @Named public class SalesFileReader implements ItemReader { ... private BufferedReader reader; private int rowNum;
public void open(Serializable checkpoint) {
reader = Files.newBufferedReader(Paths.get(fileName)); if (checkpoint != null) { // TODO skip reader } }
public Serializable checkpointInfo() {
return rowNum; }
チェックポイント毎に呼ばれる (item-count属性に設定した間隔)
初回スタート時はnull。
リスタート時には異常終了前最後のcheckpointInfo()の値が渡される
ジョブ実装 #2-1-3 ItemReader.readItem @Named public class SalesFileReader implements ItemReader { ... public Object readItem() { String line = reader.readLine(); if (line == null) return null; // TODO1 line to Sales obj // TODO2 入力値チェック if (!isValid()) { throw new InvalidRecordException(rowNum, line); } rowNum++; return sales; }
入力値チェックもItemReaderの役割。
エラー時はスキップ対象例外を返す。
入力がなくなったらnullを返す
ジョブ実装 #2-1-4 ItemReader.close @Named public class SalesFileReader implements ItemReader {
...
public void open(Serializable checkpoint) { // 済 }
public Serializable checkpointInfo() { //済 }
public Object readItem() throws Exception { // 済 }
public void close() {
reader.close(); } ...
リソースクローズ処理。 readItem()から null が返される or 異常終了時に呼ばれる。
AbstractItemReaderにより省略可
@Named public class SalesFileReader extends AbstractItemReader {
@Override
public Object readItem() throws Exception { ... }
}
一部メソッドは実装不要時に使用。readItem()のみ必須。
ジョブ実装 #2-2 ItemProcessorの実装
• ItemReaderで読んだ値を引数に処理
• ビジネスロジック実装部分
【Step1】 請求額確定
CSV aa,bb
DBMS
入力 出力
請求額TBL
Bill Processor
SalesFile Reader
Bill Writer
LogErrorRecordListener 【Step2】 請求書生成
【Step3】 メール送付
済
ジョブ実装 #2-2-1 ItemProcessorの実装
@Named public class BillProcessor implements ItemProcessor {
@Override
public Object processItem(Object item) throws Exception { Sales sales = (Sales) item; // ビジネスロジックをここに実装 // TODO 売上データ ⇒ 請求額 算出 return bill; }
}
ビジネスロジックをprocessItem()に実装する
ジョブ実装 #2-3 ItemWriterの実装
• ItemProcessorで処理した値を引数に
• データを書き出す部分
【Step1】 請求額確定
CSV aa,bb
DBMS
入力 出力
請求額TBL
Bill Processor
SalesFile Reader
Bill Writer
LogErrorRecordListener 【Step2】 請求書生成
【Step3】 メール送付
済 済
ジョブ実装 #2-3-1 ItemWriterの実装
@Named public class BillWriter implements ItemWriter {
public void open(Serializable checkpoint) { }
public void writeItems(List<Object> items) throws Exception { }
public void close() { }
public Serializable checkpointInfo() { }
}
ItemReaderとの違いはwriteItemsのみ
ジョブ実装 #2-3-1 ItemWriterの実装
@Named public class BillWriter implements ItemWriter {
@Override
public void writeItems(List<Object> items) throws Exception { List<Bill> bills = (List<Bill>) items; bills.stream() .forEach(this::insertToDB); }
private void insertToDB(Bill bill) { .. }
... }
複数のRead => Process 結果のリストが引数に設定
コミットはwriteItemメソッドの呼び出し毎に実行
ジョブ実装 #2-4 スキップリスナ
• スキップ対象例外の発生時に呼ばれる
• エラーレコードをロギングしたい
【Step1】 請求額確定
CSV aa,bb
DBMS
入力 出力
請求額TBL
Bill Processor
SalesFile Reader
BillAmount Writer
LogErrorRecordListener 【Step2】 請求書生成
【Step3】 メール送付
済 済 済
ジョブ実装 #2-4-1 スキップリスナの実装
@Named public class LogErrorRecordListener implements SkipReadListener { private static final Logger log = ...;
@Override
public void onSkipReadItem(Exception e) { InvalidRecordException ex = (InvalidRecordException) e; log.warn(“Row ”+ ex.getNum() + “ is Invalid Record.”); }
} スキップした行情報のロギング。 スキップした行全体を別ファイルに書出すのもあり。
ジョブ実装 #3 ジョブの起動
• JobOperator経由で起動する
• EJBタイマ、JAX-RS/Servletなど で起動
【Step1】 請求額確定
Bill Processor
SalesFile Reader
BillAmount Writer
LoggingErrorRecordListener 【Step2】 請求書生成
【Step3】 メール送付
請求書発行ジョブ
済
済 済 済
JobOperator
EJBタイマ
JAX-RS/Servlet
ジョブ実装 #3-1 ジョブの起動
@ScheduleによるEJB タイマー
@javax.ejb.Singleton public class ScheduleTimer {
@Schedule(hour=“4”, minute=“2”)
public void timeout() { // start JobOperator jobOperator = BatchRuntime.getJobOperator(); Property props = new Property(); props.setProperty(“fileName”, “/var/xxx/input.csv”); jobOperator.start(“issueBill”, props); } } ジョブIDと、ジョブプロパティを引数に起
動
ジョブ実装 #3-2 ジョブの起動
EEサーバ上なのでJAX-RS経由でも起動できる
@Path(“/jobs/{jobId}”) public class BatchResource {
@POST
public void start(@PathParam String jobId) { // start JobOperator jobOperator = BatchRuntime.getJobOperator(); Property props = new Property(); props.setProperty(“fileName”, “/var/xxx/input.csv”); jobOperator.start(“issueBill”, props); } ...
ジョブ実装 #3-3 ジョブの停止
startの返り値のジョブ実行IDを引数に停止
@Path(“/jobs/{jobId}”) public class BatchResource {
@POST
public void start(@PathParam String jobId) { // start JobOperator jobOperator = BatchRuntime.getJobOperator(); long execId = jobOperator.start(jobId, props); // stop jobOperator.stop(execId); } ...
ジョブ実装 #3-4 ジョブの再実行
Reader/Writerのopen引数にチェックポイントが設定
@Path(“/jobs/{jobId}”) public class BatchResource {
@POST
public void start(@PathParam String jobId) { // start JobOperator jobOperator = BatchRuntime.getJobOperator(); long execId = jobOperator.start(jobId, props); // restart jobOperator.restart(execId, props); } ...
まとめ : jBatch利用の流れ
1. ジョブ設計
• ジョブを抽出。ステップと入出力を定義する。
2. ジョブ実装
• ステップの流れ、プロパティをジョブXMLに定義
• ItemReader/ItemProcessor/ItemWriterのJava実装
• JobOperator経由で起動、停止、リスタート制御
• EJBタイマ、JAX-RS Web-API 等 から起動
本日のコンテンツ
• jBatchとは何か
• jBatchをどうやって使うのか
• メリット と デメリット
• よくある疑問
• まとめ
jBatch適用のメリット
• 一定の型に填めて開発できる
• 1つ1つのジョブ構造のAP設計簡略化
• 第3者(保守・改造時) が構造を理解しやすい
• チェックポイント等のバッチ共通機能の恩恵
• Java EE サーバの機能が使える
• JAX-RS(REST入出力) / JMS連携 / CDIによるDI
• リソース管理: DB接続プール、スレッドプール
jBatch適用のデメリット
• 型に填めにくい処理への適用が難しい
• 複数入力(マスタ / トランザクション読込)、複数出力
• 読み込み方の工夫で解決できることもある
CSV aa,bb
商品マスタ
【Step】 売上データ集計
Processor Reader Writer
CSV aa,bb
売上データ
public class Aggregate { private Master master; private List<Transaction> trans;
1マスタ/複数トランザクションデータ毎 に読み込み
DBMS
集計済 売り上げ
jBatch適用のデメリット
• 処理によっては記述が冗長
• XMLによる処理の流れの定義
• java.nio.file.Files.linesメソッドで十分な場合もある
// CSVファイルより、15歳以上のみ抽出して別ファイルへ try (Stream<String> stream = Files.lines(Paths.get(“csv.txt”)) { stream .map(line -> line.split(“,”)) .map(arr -> // TODO array to Student) .filter(student -> student.getAge() > 15) .forEachOrdered(s -> // write to file); }
Reader
Processor Writer
本日のコンテンツ
• jBatchとは何か
• jBatchをどうやって使うのか
• メリット と デメリット
• よくある疑問
• まとめ
Question #1 Spring Batch と何が違うのか?
jBatch と SpringBatch の違い #1
• jBatchは仕様、SpringBatchは実装
• jBatch実装: RI (GlassFish), JBeret (WildFly)
• SpringBatch3.0より jBatchに準拠
• ライブラリの豊富さは先行のSpringBatch有利
SpringBatchの機能範囲
jBatch
アーキテクチャ ジョブXML API定義
• 豊富なライブラリ群 • CSV/TSV, XML, JDBCページング
• かゆいところに手が届く • 同一形式の複数ファイル読込 (stock_1.csv, stock_2.csv ...) • 複数行を1アイテムにマッピング
jBatch と SpringBatch の違い #2
• SpringBatchはジェネリクス対応
public class Reader implements ItemReader<Stock> { @Override public Stock read() { // read stock } } public class Processor implements ItemProcessor<Stock, Result> { @Override public Result process(Stock s) throws Exception { ... } }
ジェネリクス対応により キャストが不要
Question #2 もうXMLはつらいです。
Spring Batchでは Java でジョブ定義可能
@Bean public Job job() { return jobsBuilderFactory.get("myJob") .start(step1()).next(step2()) .build(); }
@Bean
protected Step step1(...) { return stepBuilderFactory.get("step1") .<Person, Person> chunk(10) .reader(reader).processor(processor).writer(writer) .build(); }
Spring Batch - Reference Documentation http://docs.spring.io/spring-batch/trunk/reference/html/configureJob.html#javaConfig
NetBeans “jBatch Suite”
• NetBeanプラグインによる jBatch サポート
• ジョブXMLの生成
• ブランク ItemXXX/Batchlet 生成 ドラック & ドロップ でステップの流れを作成
Question #3 ジョブの流れ定義は
ジョブスケジューラ or jBatch ?
やりたいことに応じて使い分ける
• システム全体のジョブ管理にはスケジューラ
• APサーバだけで完結しないジョブへの対応
• 例: ログバックアップ、サーバ再起動
• ジョブAP内の順序制御には jBatch
• Javaコード (Decision) による複雑な条件判定も可
• job.xml変更時は *.war 再デプロイが必要
jBatch実装のこれからに期待
GlassFish4はテーブル形式でジョブ履歴を表示
• 管理コンソールが強化されると jBatch はより便利に
• ジョブフロー状況の図示、ジョブ間の順序制御
Question #4 スケールアウトは可能か?
今のところ仕様上は未対応
• jBatchの仕様としては定義されていない
• 同一ジョブを複数サーバにデプロイする等の対処
• 実装サーバでの対応に期待
• クラスタ機能と連携したジョブ分散
• ジョブ毎のリソース管理 (スレッド数、Javaヒープ等)
本日のコンテンツ
• jBatchとは何か
• jBatchをどうやって使うのか
• メリット と デメリット
• よくある疑問
• まとめ
まとめ #1
JobOperator Job
Item Processor
Item Writer
<job id=“monthly_billing”> <step id=“step1” next=“step2”> <step id=“step2”> </job>
Step1 Step2 Step3
Job Repository
Item Reader
public interface ItemReader { Object readItem() throws ... }
jBatchはジョブを実装する為のフレームワーク。
まとめ #2
jBatchの利用により実現できること。
• ジョブXMLによる順序制御
• APの構造を決める (Reader/Processor/Writer)
• チェックポイント管理
• エラーハンドリング (スキップ/リトライ)
• ジョブ実装で Java EE の各種機能が使える
まとめ #3
【ここが嬉しい】
• Java EE でバッチ処理が注目され始めた
• 一定の型に填めてジョブ設計を再利用できる
• ジョブに必要な共有機能が享受できる
【もう少しな部分】
• 型に填めにくい / 塡まらない場合もある
• 現状は先発のSpring Batchの方が多機能
最後に
既にjBatch対応製品がリリースされています。
OSS製品なので是非まずは試してみてください!
OracleとJavaは、Oracle Corporation及びその子会社、関連会社の米国及びその他の国における登録商標です。 文中の社名、商品名等は各社の商標または登録商標である場合があります。
GlassFish4.1