目次
要件: チャンク タスクレットの使用方法の簡単なデモンストレーション
序章
前の記事: Spring バッチ ステップ オブジェクト - ステップ ステップとタスクレット ステップ ステップの概念とその使用法を理解した後、この記事では、Spring バッチのより広く使用され、より強力なタスクレットであるブロック内のバッチ処理ステップについて説明します。チャンク タスクレット
序章
チャンクに配置された Tasklets は、単純な Tasklets と比較して、ItemReader (読み取りモジュール)、ItemProcessor (処理モジュール)、および ItemWriter (書き込みモジュール) の 3 つのモジュールが追加されており、その名前のように、1 つはデータの読み取りを担当し、もう 1 つはデータを担当します。処理、データの書き込みを担当します。
構造図:
タイミング図:
要件: チャンク タスクレットの使用方法の簡単なデモンストレーション
ItemReader、ItemProcessor、および ItemWriter はいずれもインターフェイスであり、匿名の内部クラスを直接使用して簡単に作成できます。
package com.langfeiyes.batch._08_step_chunk_tasklet;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.launch.support.RunIdIncrementer;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.item.*;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import java.util.Arrays;
import java.util.List;
@SpringBootApplication
@EnableBatchProcessing
public class ChunkTaskletJob {
@Autowired
private JobLauncher jobLauncher;
@Autowired
private JobBuilderFactory jobBuilderFactory;
@Autowired
private StepBuilderFactory stepBuilderFactory;
@Bean
public ItemReader itemReader(){
return new ItemReader() {
@Override
public Object read() throws Exception, UnexpectedInputException, ParseException, NonTransientResourceException {
System.out.println("-------------read------------");
return "read-ret";
}
};
}
@Bean
public ItemProcessor itemProcessor(){
return new ItemProcessor() {
@Override
public Object process(Object item) throws Exception {
System.out.println("-------------process------------>" + item);
return "process-ret->" + item;
}
};
}
@Bean
public ItemWriter itemWriter(){
return new ItemWriter() {
@Override
public void write(List items) throws Exception {
System.out.println(items);
}
};
}
@Bean
public Step step1(){
return stepBuilderFactory.get("step1")
.chunk(3) //设置块的size为3次
.reader(itemReader())
.processor(itemProcessor())
.writer(itemWriter())
.build();
}
//定义作业
@Bean
public Job job(){
return jobBuilderFactory.get("step-chunk-tasklet-job")
.start(step1())
.incrementer(new RunIdIncrementer())
.build();
}
public static void main(String[] args) {
SpringApplication.run(ChunkTaskletJob.class, args);
}
}
実行後の結果
-------------read------------
-------------read------------
-------------read------------
-------------process------------>read-ret
-------------process------------>read-ret
-------------process------------>read-ret
[process-ret->read-ret, process-ret->read-ret, process-ret->read-ret]
-------------read------------
-------------read------------
-------------read------------
-------------process------------>read-ret
-------------process------------>read-ret
-------------process------------>read-ret
[process-ret->read-ret, process-ret->read-ret, process-ret->read-ret]
-------------read------------
-------------read------------
-------------read------------
-------------process------------>read-ret
-------------process------------>read-ret
-------------process------------>read-ret
[process-ret->read-ret, process-ret->read-ret, process-ret->read-ret]
....
上記の印刷された結果を観察し、2 つの結論を引き出します。
1> プログラムはループで印刷を行っています. 最初にリーダーがループで 3 回印刷され、次にプロセッサがループで 3 回印刷され、最後に一度に 3 つの値が出力されます。
2>上記の手順を無限ループで繰り返します
問題は、この効果がなぜ発生し、どうすれば改善できるのかということです。
実はこれが ChunkTasklet の実行特性で、ItemReader は null を返すまでループして読み込みます。同じことがプロセッサにも当てはまり、itemReader が何回読み取り、何回処理し、itemWriter が現在のすべての入力データを一度に出力します。
上記のケースを改善して、3 回の読み取りのみが必要になるようにしましょう。itemReader メソッドを変更するだけです。
int timer = 3;
@Bean
public ItemReader itemReader(){
return new ItemReader() {
@Override
public Object read() throws Exception, UnexpectedInputException, ParseException, NonTransientResourceException {
if(timer > 0){
System.out.println("-------------read------------");
return "read-ret-" + timer--;
}else{
return null;
}
}
};
}
結果はもはや無限ループではありません
-------------read------------
-------------read------------
-------------read------------
-------------process------------>read-ret-3
-------------process------------>read-ret-2
-------------process------------>read-ret-1
[process-ret->read-ret-3, process-ret->read-ret-2, process-ret->read-ret-1]
タイマーが 10 に変更され、.chunk(3)の結果が変更されない場合はどうなるでしょうか?
-------------read------------
-------------read------------
-------------read------------
-------------process------------>read-ret-10
-------------process------------>read-ret-9
-------------process------------>read-ret-8
[process-ret->read-ret-10, process-ret->read-ret-9, process-ret->read-ret-8]
-------------read------------
-------------read------------
-------------read------------
-------------process------------>read-ret-7
-------------process------------>read-ret-6
-------------process------------>read-ret-5
[process-ret->read-ret-7, process-ret->read-ret-6, process-ret->read-ret-5]
-------------read------------
-------------read------------
-------------read------------
-------------process------------>read-ret-4
-------------process------------>read-ret-3
-------------process------------>read-ret-2
[process-ret->read-ret-4, process-ret->read-ret-3, process-ret->read-ret-2]
-------------read------------
-------------process------------>read-ret-1
[process-ret->read-ret-1]
パターンは見つかりましたか?
chunkSize = 3 の場合、リーダーが最初に 3 回読み取り、それをプロセッサに送信して 3 回処理し、最後にライターが 3 つの値を出力することを意味します。
timer =10 は、10 個のデータがあり、1 つのバッチ (パス) は 3 個のデータしか処理できず、処理するのに 4 つのバッチ (パス) が必要であることを意味します。
バッチ臭は出ますか?
結論: chunkSize は、1 回のトリップで ItemReader が何回読み取る必要があり、ItemProcessor が何回処理する必要があるかを意味します。
チャンクジェネリック
上記の場合、デフォルトでは Object 型を使用してデータの読み取り、書き込み、および処理を行いますが、Item のデータ型を指定すると、特定の操作のジェネリック型を指定できます。
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.launch.support.RunIdIncrementer;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.item.*;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import java.util.List;
//开启 spring batch 注解--可以让spring容器创建springbatch操作相关类对象
@EnableBatchProcessing
//springboot 项目,启动注解, 保证当前为为启动类
@SpringBootApplication
public class ChunkTaskletJob {
//作业启动器
@Autowired
private JobLauncher jobLauncher;
//job构造工厂---用于构建job对象
@Autowired
private JobBuilderFactory jobBuilderFactory;
//step 构造工厂--用于构造step对象
@Autowired
private StepBuilderFactory stepBuilderFactory;
int timer = 10;
//读操作
@Bean
public ItemReader<String> itemReader(){
return new ItemReader<String>() {
@Override
public String read() throws Exception, UnexpectedInputException, ParseException, NonTransientResourceException {
if(timer > 0){
System.out.println("-------------read------------");
return "read-ret-->" + timer--;
}else{
return null;
}
}
};
}
//处理操作
@Bean
public ItemProcessor<String, String> itemProcessor(){
return new ItemProcessor<String, String>() {
@Override
public String process(String item) throws Exception {
System.out.println("-------------process------------>" + item);
return "process-ret->" + item;
}
};
}
//写操作
@Bean
public ItemWriter<String> itemWriter(){
return new ItemWriter<String>() {
@Override
public void write(List<? extends String> items) throws Exception {
System.out.println(items);
}
};
}
//构造一个step对象--chunk
@Bean
public Step step1(){
//tasklet 执行step逻辑, 类似 Thread()--->可以执行runable接口
return stepBuilderFactory.get("step1")
.<String, String>chunk(3) //暂时为3
.reader(itemReader())
.processor(itemProcessor())
.writer(itemWriter())
.build();
}
@Bean
public Job job(){
return jobBuilderFactory.get("chunk-tasklet-job")
.start(step1())
.incrementer(new RunIdIncrementer())
.build();
}
public static void main(String[] args) {
SpringApplication.run(ChunkTaskletJob.class, args);
}
}
ここまでで、この記事は終わりです.次に何が起こるか知りたい場合は、次の章を聞いて分解してください〜
動画版へ
テキストを読むのに慣れていない場合は、ビデオ バージョンに切り替えることができます: Spring Batch の効率的なバッチ処理フレームワークの実践