1. 概述
Spring Batch提供了多種方式用於處理並行,提高性能。主要分為2大類:
- 單個進程,多線程
- 多個進程
因此,可以細分為以下幾類:
- 多線程Step(Multi-thread Step,single process)
- 並行Step(Parallel Steps, single process )
- Remote Chunking of Step( multi process)
- Partitioning a step(single or multi process)
2. Multi-Thread Step
最直接的方式是給Step配置一個TaskExecutor
<step id="loading"> <tasklet task-executor="taskExecutor">...</tasklet> </step>
此時,taskExecutor的線程並行來執行Item處理(統一item的read,process,write在同一個線程中執行)。可以限制TaskExecutor的閾值(默認為4):
<step id="loading"> <tasklet task-executor="taskExecutor" throttle-limit="20">...</tasklet> </step>
需要注意的是,在多線程Step中,需要確保Reader、Processor和Writer是線程安全的,否則容易出現並發問題。Spring Batch提供的大部分組件都是非線程安全的,他們都保存有部分狀態信息,主要是為了支持任務重啟。
因此,使用多線程Step的核心任務是實現無狀態化,例如不保存當前讀取的item的cursor,而是同item的flag字段來區分item是否被處理過,已經被處理過的下次重啟的時候,直接被過濾掉。
多線程Step實現的是單個Step的多線程化。
3. Parallel Steps
如果多個Step沒有先后關系,可以並行執行,這是通過split和flow來實現的:
<job id="job1">
<split id="split1" task-executor="taskExecutor" 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"/>
</job>
<beans:bean id="taskExecutor" class="org.spr...SimpleAsyncTaskExecutor"/>
該種模式提供的是多個Step的並行處理。
4. Remote Chunking
Remote chunking 的示意圖如下:
Master為單個進程,因此只有在處理所需要的時間遠遠大於讀取所需要的時間的時候,這個方式才適用,否則Master容易成為瓶頸。
Master是一個常規的Step實現,只不過它的ItemWriter知道如何將Items分塊,並發送到中間件(例如JMS),通過實現ChunkProvider接口來實現。
public interface ChunkProvider<T>{ Chunk<T> provide(StepContribution contribution) throws Exception; void postProcess(StepContribution contribution, Chunk<T> chunk); }
Slave則充當中間件的Listener,通過ItemProcessor和ItemWriter來實現item處理,具體的是通過實現ChunkProcessor接口
public interface ChunkProcessr<T> { void process(StepContribution contribution,Chunk<T> chunk) throws Exception; }
可以看到,remote chunking實現的是(Processor、Writer)的並行化。分區不需要對數據源的結構有很明確的了解。
5. Partitioning
Step分區處理示意圖如下:
一個分區配置如下:
<step id="step1.master"> <partition step="step1" partitioner="partitioner"> <handler grid-size="10" task-executor="taskExecutor"/> </partition> </step> <step id="step1"> <tasklet> <chunk reader="" writer"" processor="" .../> </tasklet> </step>
主要包括2個步驟:
1. 數據分區
2. 分區處理
具體的分區執行流程如下:
PartitionHandler
其中PartitionHandler知道集群環境,根據下面要介紹的Splitter進行分區,發送執行請求(通過WebService ,RMI等方式) 並收集執行結果,聚合,最終反饋給Job。Spring Batch提供了一個同一台機器上的Handler實現,在同一機器上創建多個Step Execution。
<step id="step1.master">
<partition step="step1" handler="handler"/>
</step>
<bean class="org.spr...TaskExecutorPartitionHandler">
<property name="taskExecutor" ref="taskExecutor"/>
<property name="step" ref="step1" />
<property name="gridSize" value="10" />
</bean>
Partitioner
Partitioner負責生成執行上下文,作為Step Execution的輸入參數,其接口定義如下:
public interface Partitioner { Map<String, ExecutionContext> partition(int gridSize); }
返回結果中Map的key,是一個唯一的名字,常見的實現方式是step_name + counter。或者通過PartitioneNameProvider來提供。 名字關聯到對應的執行上下文。ExecutionContext只是一個key/value容器,因此它可能包含主鍵范圍,行數等信息。
StepExecutionSplitter
Partitioner生成的ExecutionContext,經過StepExecutionSplitter處理之后形成StepExecution,然后交給Handler處理。StepExecutionSplitter接口定義如下:
public interface StepExecutionSplitter { String getStepName(); Set<StepExecution> split(StepExecution stepExecution , int gridSize) throws JobExecutionException; }
通常,Slave中的Step配置都是相同的,他們通過獲取Partitioner划分好的ExecutionContext,獲取Step的輸入參數,動態綁定到Step中。例如划分的情況如下表:
step execution name(key) | ExecutionContext(value) |
---|---|
filecopy:partition0 | file_name=/home/data/0 |
filecopy:partition1 | file_name=/home/data/1 |
filecopy:partition2 | file_name=/home/data/2 |
然后該文件名被綁定到Step的組件中:
<bean id="itemReader" scope="step"
class="org.spr...MultiResourceItemReader">
<property name="resource" value="#{stepExecutionContext[file_name]}/*"/>
</bean>
整個具體流程如下:
可以看出,Patitioning提供的是(Reader、Processor、Writer)的並行化。分區模式需要對數據源的結構有一定的了解,比如知道主鍵范圍。
本文轉自:https://blog.csdn.net/bingduanlbd/article/details/50989664