Spring Batch Partitionierungsbeispiel

Spring Batch Partitioning Beispiel

spring batch partitioning

Fotokredit:Spring Source

In Spring Batch ist "Partitionierung" "mehrere Threads, um jeweils einen Datenbereich zu verarbeiten". Angenommen, Sie haben 100 Datensätze in einer Tabelle, der eine „primäre ID“ von 1 bis 100 zugewiesen ist, und Sie möchten die gesamten 100 Datensätze verarbeiten.

Normalerweise startet der Prozess von 1 bis 100, ein Beispiel für einen einzelnen Thread. Es wird geschätzt, dass der Vorgang 10 Minuten dauert.

Single Thread - Process from 1 to 100

In "Partitionierung" können wir 10 Threads starten, um jeweils 10 Datensätze zu verarbeiten (basierend auf dem Bereich von "id"). Jetzt kann der Vorgang nur noch 1 Minute dauern.

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

Um die Partitionierungstechnik zu implementieren, müssen Sie die Struktur der zu verarbeitenden Eingabedaten verstehen, damit Sie den „Datenbereich“ richtig planen können.

1. Lernprogramm

In diesem Tutorial zeigen wir Ihnen, wie Sie einen "Partitioner" -Job mit 10 Threads erstellen. Jeder Thread liest Datensätze aus der Datenbank basierend auf dem angegebenen Bereich von "id".

Verwendete Tools und Bibliotheken

  1. Maven 3

  2. Eclipse 4.2

  3. JDK 1.6

  4. Federkern 3.2.2.FREIGABE

  5. Spring Batch 2.2.0.RELEASE

  6. MySQL Java Driver 5.1.25

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

Benutzer Tabellenstruktur

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. Projektverzeichnisstruktur

Überprüfen Sie die endgültige Projektstruktur, ein Standard-Maven-Projekt.

spring-batch-partitioner-before

3. Partitionierer

Erstellen Sie zunächst einePartitioner-Implementierung und setzen Sie die "partitioning range" in dieExecutionContext. Später deklarieren Sie die gleichenfromId undtied in der Batch-Job-XML-Datei.

In diesem Fall sieht der Partitionierungsbereich folgendermaßen aus:

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. Batch-Jobs

Überprüfen Sie die XML-Datei des Batch-Jobs. Sie sollte selbsterklärend sein. Einige Punkte zum Hervorheben:

  1. Für Partitionierergrid-size = number of threads.

  2. Für die pagingItemReader-Bean, ein Beispiel für einen JDBC-Reader, werden die#{stepExecutionContext[fromId, toId]}-Werte durch dieExecutionContext in rangePartitioner eingefügt.

  3. Bei der itemProcessor-Bean werden die#{stepExecutionContext[name]}-Werte durch dieExecutionContext in rangePartitioner eingefügt.

  4. Für Autoren gibt jeder Thread die Datensätze in einer anderen CSV-Datei mit dem Dateinamenformat -users.processed[fromId]}-[toId].csv aus.

job-partitioner.xml



  
  

  
  

  
  

    
    
    
        
    
    

  

  
  
    
        
    
  

  

  

  
  
    
  

  
    
    
      
        
        
        
        
        
      
    
    
    
      
        
        
      
    
    
    
        
    
  

  
  
    
    
    
      
        
        
          
            
          
        
      
    
  

Die Elementverarbeitungsklasse wird nur zum Ausdrucken des Verarbeitungselements und des aktuell ausgeführten "Threadnamens" verwendet.

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. Starte es

Lädt alles und führt es aus ... 10 Threads werden gestartet, um den bereitgestellten Datenbereich zu verarbeiten.

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");

  }
}

Konsolenausgabe

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
......

Nach Abschluss des Vorgangs werden 10 CSV-Dateien erstellt.

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;
    }

}

Denken Sie daran, das automatische Scannen der Spring-Komponente zu aktivieren.

    

6.2 Database partitioner reader - MongoDB example.

job-partitioner.xml

  
    
    
    
    
        
            
        
    
  

Erledigt.

Quellcode herunterladen

Laden Sie es herunter -SpringBatch-Partitioner-Example.zip (31 KB)