目录

  • 1、Spring Batch的四种模式
  • 1.1、Multi-threaded Step
  • 1.2、 Parallel Step
  • 1.3、 Remote Chunking of Step
  • 1.4、Partitioning Step

1、Spring Batch的四种模式

根据官网解读,Spring Batch有以下四种模式:

  • Multi-threaded Step
  • Parallel Step
  • Remote Chunking of Step
  • Partitioning Step
1.1、Multi-threaded Step

多线程Step,根据官网给出的例子,它的定义如下:

@Bean
public TaskExecutor taskExecutor(){
    return new SimpleAsyncTaskExecutor("spring_batch");
}

@Bean
public Step sampleStep(TaskExecutor taskExecutor) {
	return this.stepBuilderFactory.get("sampleStep")
				.<String, String>chunk(5)
				.reader(itemReader())
				.writer(itemWriter())
				.taskExecutor(taskExecutor)
				.build();
}

上面这段代码配置就是用SimpleAsyncTaskExecutor线程池来进行一个多线程的读取,但是这个多线程并不是我们以前认为的一个线程处理一个setp,这里的多线程其实体现在chunk上面,就是说采用多个线程去进行数据的读取,等所有线程的reader操作完成后,然后将所有的数据封装成一个参数传给Writer,然后由writer进行提交。看下面这个模型图可能就会理解了。

springbatch flow 并行 springbatch step_多线程


注意:如果这里的线程池不是SimpleAsyncTaskExecutor,而是ThreadPoolTaskExecutor,那么此时运行的线程并不是上面代码定义的5个,而是4个。

1.2、 Parallel Step

真正的并发Step,现在来看一下关网的demo:

@Bean
public Job job() {
    return jobBuilderFactory.get("job")
        .start(splitFlow())
        .next(step4())
        .build()        //builds FlowJobBuilder instance
        .build();       //builds Job instance
}

@Bean
public Flow splitFlow() {
    return new FlowBuilder<SimpleFlow>("splitFlow")
        .split(taskExecutor())
        .add(flow1(), flow2())
        .build();
}

@Bean
public Flow flow1() {
    return new FlowBuilder<SimpleFlow>("flow1")
        .start(step1())
        .next(step2())
        .build();
}

@Bean
public Flow flow2() {
    return new FlowBuilder<SimpleFlow>("flow2")
        .start(step3())
        .build();
}

@Bean
public TaskExecutor taskExecutor(){
    return new SimpleAsyncTaskExecutor("spring_batch");
}

在上面这个例子中,是通过split关键字来实现并发的,但是需要注意一点的是只有Step1、Step2、Step3全部执行成功后,才会继续执行Step4。把上面的例子转化为模型图,相信大家一下就能看明白了:

springbatch flow 并行 springbatch step_java_02

1.3、 Remote Chunking of Step

官方描述如下:在远程分块中,Step处理过程分为多个进程,并通过某种中间件相互通信。下图显示了该模式:

springbatch flow 并行 springbatch step_spring_03


从这张图片上来看,可以很容易的知道,该模式适合主从结构场景,整个过程大概如下:Master Step先读取数据,然后将这个数据交给某个中间件,然后Slave Step对这个中间件进行监听,当监听到有自己需要的数据的时候就从中间件拿数据,然后将数据写入到指定的地方。但是这个模型在使用的时候需要考虑负载均衡的问题。

1.4、Partitioning Step

这个模型真正意义上实现了Step的多线程,每个线程都有一个Reader、Writer,相当于每一个线程有一个Step,但是该模型真正的好处是将需要处理的数据先进行分割,然后每一个reader、writer模块对应一个数据区,这样保证了数据的同步也充分发挥了多线程的优势。先看下官网的定义:

springbatch flow 并行 springbatch step_java_04


每一个Worker相当于一个Reader和Writer,每一个worker的执行流程如下:

springbatch flow 并行 springbatch step_springbatch flow 并行_05


首先执行handler,这个hanlder的意义就是处理远程数据的划分,其实就是设置TaskExecutorPartitionHandler 参数,参数设置完成后,就调用spit函数,采用多线程的方式执行Step里面的reader和Writter操作。全部执行完成后将数据返回给下一个Step。参数配置代码如下所示:

/**
     * Step配置
     * @return
     */
    @Bean
    public Step step1Manager() {
        return stepBuilderFactory.get("step1.manager")
                .partitioner("step1", testPartitioner())
                .partitionHandler(partitionHandler())
                .build();
    }

    /**
     * handler函数的配置
     * @return
     */
    @Bean
    public PartitionHandler partitionHandler() {
        TaskExecutorPartitionHandler retVal = new TaskExecutorPartitionHandler();
        retVal.setTaskExecutor(taskExecutor());
        retVal.setStep(step1());
        retVal.setGridSize(10);
        return retVal;
    }
    
   /**
     * 设置Partitioner  数据的具体划分
     */
    public class testPartitioner implements Partitioner {
        @Override
        public Map<String, ExecutionContext> partition(int gridSize) {
            Map<String, ExecutionContext> result = new HashMap<>();
            for (int i = 1; i <= gridSize; i++) {
                ExecutionContext value = new ExecutionContext();
                result.put("partition " + i, value);
            }
            return result;
    }
    @Bean
    public TaskExecutor taskExecutor(){
    return new SimpleAsyncTaskExecutor("spring_batch");
     }
    @Bean
    public MultiResourceItemReader itemReader
    (@Value("#{stepExecutionContext['fileName']}/*") Resource [] resources) 
    {
	return new MultiResourceItemReaderBuilder<String>()
			.delegate(fileReader())
			.name("itemReader")
			.resources(resources)
			.build();
      }