Пример Spring Batch Partitioning

Пример Spring Batch Partitioning

spring batch partitioning

Фото:Spring Source

В Spring Batch «Секционирование» - это «несколько потоков для обработки каждого диапазона данных». Например, предположим, у вас есть 100 записей в таблице, которой назначен «первичный идентификатор» от 1 до 100, и вы хотите обработать все 100 записей.

Обычно процесс начинается с 1 до 100, пример с одним потоком. Предполагается, что процесс займет 10 минут.

Single Thread - Process from 1 to 100

В разделе «Разделение» мы можем запустить 10 потоков для обработки 10 записей в каждой (в зависимости от диапазона «id»). Теперь процесс может занять всего 1 минуту.

Thread 1 - Process from 1 to 10
Thread 2 - Process from 11 to 20
Thread 3 - Process from 21 to 30
......
Thread 9 - Process from 81 to 90
Thread 10 - Process from 91 to 100

Чтобы реализовать метод «Разделение», вы должны понимать структуру входных данных для обработки, чтобы правильно планировать «диапазон данных».

1. Руководство

В этом руководстве мы покажем вам, как создать задание «Partitioner», которое имеет 10 потоков, каждый поток будет считывать записи из базы данных на основе предоставленного диапазона «id».

Использованные инструменты и библиотеки

  1. Maven 3

  2. Затмение 4.2

  3. JDK 1.6

  4. Spring Core 3.2.2.RELEASE

  5. Spring Batch 2.2.0.RELEASE

  6. MySQL Java Driver 5.1.25

P.S Assume “users” table has 100 records.

структура таблицы пользователей

id, user_login, user_passs, age

1,user_1,pass_1,20
2,user_2,pass_2,40
3,user_3,pass_3,70
4,user_4,pass_4,5
5,user_5,pass_5,52
......
99,user_99,pass_99,89
100,user_100,pass_100,76

2. Структура каталога проекта

Просмотрите окончательную структуру проекта, стандартный проект Maven.

spring-batch-partitioner-before

3. Разметка

Сначала создайте реализациюPartitioner, поместив «partitioning range» вExecutionContext. Позже вы объявите те же самыеfromId иtied в XML-файле пакетного задания.

В этом случае диапазон разделения выглядит следующим образом:

Thread 1 = 1 - 10
Thread 2 = 11 - 20
Thread 3 = 21 - 30
......
Thread 10 = 91 - 100

RangePartitioner.java

package com.example.partition;

import java.util.HashMap;
import java.util.Map;

import org.springframework.batch.core.partition.support.Partitioner;
import org.springframework.batch.item.ExecutionContext;

public class RangePartitioner implements Partitioner {

    @Override
    public Map partition(int gridSize) {

        Map result
                       = new HashMap();

        int range = 10;
        int fromId = 1;
        int toId = range;

        for (int i = 1; i <= gridSize; i++) {
            ExecutionContext value = new ExecutionContext();

            System.out.println("\nStarting : Thread" + i);
            System.out.println("fromId : " + fromId);
            System.out.println("toId : " + toId);

            value.putInt("fromId", fromId);
            value.putInt("toId", toId);

            // give each thread a name, thread 1,2,3
            value.putString("name", "Thread" + i);

            result.put("partition" + i, value);

            fromId = toId + 1;
            toId += range;

        }

        return result;
    }

}

4. Пакетные работы

Просмотрите XML-файл пакетного задания, он не требует пояснений. Несколько моментов, чтобы выделить:

  1. Для разделителяgrid-size = number of threads.

  2. Для bean-компонента pagingItemReader, примера чтения jdbc, значения#{stepExecutionContext[fromId, toId]} будут введеныExecutionContext в rangePartitioner.

  3. Для bean-компонента itemProcessor значения#{stepExecutionContext[name]} будут введеныExecutionContext в rangePartitioner.

  4. Для писателей каждый поток будет выводить записи в разных файлах csv с форматом имени файла -users.processed[fromId]}-[toId].csv.

job-partitioner.xml




  
  

  
  

  
  

    
    
    
        
    
    

  

  
  
    
        
    
  

  

  

  
  
    
  

  
    
    
      
        
        
        
        
        
      
    
    
    
      
        
        
      
    
    
    
        
    
  

  
  
    
    
    
      
        
        
          
            
          
        
      
    
  

Класс обработчика элементов используется для печати только элемента обработки и текущего запущенного «имени потока».

UserProcessor.java - item processor

package com.example.processor;

import org.springframework.batch.item.ItemProcessor;
import com.example.User;

public class UserProcessor implements ItemProcessor {

    private String threadName;

    @Override
    public User process(User item) throws Exception {

        System.out.println(threadName + " processing : "
            + item.getId() + " : " + item.getUsername());

        return item;
    }

    public String getThreadName() {
        return threadName;
    }

    public void setThreadName(String threadName) {
        this.threadName = threadName;
    }

}

5. Запустить его

Загружает все и запускает… 10 потоков будут запущены для обработки предоставленного диапазона данных.

package com.example;

import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class PartitionApp {

  public static void main(String[] args) {
    PartitionApp obj = new PartitionApp ();
    obj.runTest();
  }

  private void runTest() {

    String[] springConfig = { "spring/batch/jobs/job-partitioner.xml" };

    ApplicationContext context = new ClassPathXmlApplicationContext(springConfig);

    JobLauncher jobLauncher = (JobLauncher) context.getBean("jobLauncher");
    Job job = (Job) context.getBean("partitionJob");

    try {

      JobExecution execution = jobLauncher.run(job, new JobParameters());
      System.out.println("Exit Status : " + execution.getStatus());
      System.out.println("Exit Status : " + execution.getAllFailureExceptions());

    } catch (Exception e) {
        e.printStackTrace();
    }

      System.out.println("Done");

  }
}

Консольный вывод

Starting : Thread1
fromId : 1
toId : 10

Starting : Thread2
fromId : 11
toId : 20

Starting : Thread3
fromId : 21
toId : 30

Starting : Thread4
fromId : 31
toId : 40

Starting : Thread5
fromId : 41
toId : 50

Starting : Thread6
fromId : 51
toId : 60

Starting : Thread7
fromId : 61
toId : 70

Starting : Thread8
fromId : 71
toId : 80

Starting : Thread9
fromId : 81
toId : 90

Starting : Thread10
fromId : 91
toId : 100

Thread8 processing : 71 : user_71
Thread2 processing : 11 : user_11
Thread3 processing : 21 : user_21
Thread10 processing : 91 : user_91
Thread4 processing : 31 : user_31
Thread6 processing : 51 : user_51
Thread5 processing : 41 : user_41
Thread1 processing : 1 : user_1
Thread9 processing : 81 : user_81
Thread7 processing : 61 : user_61
Thread2 processing : 12 : user_12
Thread7 processing : 62 : user_62
Thread6 processing : 52 : user_52
Thread1 processing : 2 : user_2
Thread9 processing : 82 : user_82
......

После завершения процесса будет создано 10 файлов CSV.

spring-batch-partitioner-after

users.processed1-10.csv

1,user_1,pass_1,20
2,user_2,pass_2,40
3,user_3,pass_3,70
4,user_4,pass_4,5
5,user_5,pass_5,52
6,user_6,pass_6,69
7,user_7,pass_7,48
8,user_8,pass_8,34
9,user_9,pass_9,62
10,user_10,pass_10,21

6. Misc

6.1 Alternatively, you can inject the #{stepExecutionContext[name]} via annotation.

UserProcessor.java - Annotation version

package com.example.processor;

import org.springframework.batch.item.ItemProcessor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import com.example.User;

@Component("itemProcessor")
@Scope(value = "step")
public class UserProcessor implements ItemProcessor {

    @Value("#{stepExecutionContext[name]}")
    private String threadName;

    @Override
    public User process(User item) throws Exception {

        System.out.println(threadName + " processing : "
                     + item.getId() + " : " + item.getUsername());

        return item;
    }

}

Не забудьте включить автоматическое сканирование компонента Spring.

    

6.2 Database partitioner reader - MongoDB example.

job-partitioner.xml

  
    
    
    
    
        
            
        
    
  

Готово.

Скачать исходный код

Скачать -SpringBatch-Partitioner-Example.zip (31 КБ)