Exemple de partitionnement par lots de printemps

Exemple de partitionnement Spring Batch

spring batch partitioning

Crédit photo:Spring Source

Dans Spring Batch, le «partitionnement» est «plusieurs threads pour traiter chacun une plage de données». Par exemple, supposons que vous ayez 100 enregistrements dans une table, à laquelle «ID principal» est attribué de 1 à 100, et que vous souhaitez traiter l'intégralité des 100 enregistrements.

Normalement, le processus démarre de 1 à 100, un exemple de thread unique. Le processus devrait durer 10 minutes environ.

Single Thread - Process from 1 to 100

Dans «Partitionnement», nous pouvons démarrer 10 threads pour traiter 10 enregistrements chacun (en fonction de la plage de «id»). Maintenant, le processus peut prendre seulement 1 minute pour se terminer.

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

Pour implémenter la technique de «partitionnement», vous devez comprendre la structure des données d'entrée à traiter, afin de pouvoir planifier correctement la «plage de données».

1. Didacticiel

Dans ce didacticiel, nous vous montrerons comment créer un travail de «partitionnement», qui a 10 threads, chaque thread lira les enregistrements de la base de données, en fonction de la plage d’id fournie.

Outils et bibliothèques utilisés

  1. Maven 3

  2. Eclipse 4.2

  3. JDK 1.6

  4. Spring Core 3.2.2.RELEASE

  5. Spring Batch 2.2.0.RELEASE

  6. Pilote Java MySQL 5.1.25

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

structure de la table des utilisateurs

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. Structure du répertoire du projet

Passez en revue la structure finale du projet, un projet Maven standard.

spring-batch-partitioner-before

3. Partitionneur

Tout d'abord, créez une implémentation dePartitioner, placez les «partitioning range» dans lesExecutionContext. Plus tard, vous déclarerez les mêmesfromId ettied dans le fichier XML du travail par lots.

Dans ce cas, la plage de partitionnement ressemble à ceci:

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. Emplois par lots

Examinez le fichier XML du travail par lots, il devrait être explicite. Quelques points à souligner:

  1. Pour le partitionneur,grid-size = number of threads.

  2. Pour le bean pagingItemReader, un exemple de lecteur jdbc, les valeurs#{stepExecutionContext[fromId, toId]} seront injectées par lesExecutionContext dans rangePartitioner.

  3. Pour le bean itemProcessor, les valeurs#{stepExecutionContext[name]} seront injectées par lesExecutionContext dans rangePartitioner.

  4. Pour les écrivains, chaque thread affichera les enregistrements dans un fichier csv différent, avec un format de nom de fichier -users.processed[fromId]}-[toId].csv.

job-partitioner.xml




  
  

  
  

  
  

    
    
    
        
    
    

  

  
  
    
        
    
  

  

  

  
  
    
  

  
    
    
      
        
        
        
        
        
      
    
    
    
      
        
        
      
    
    
    
        
    
  

  
  
    
    
    
      
        
        
          
            
          
        
      
    
  

La classe de processeur d'élément est utilisée pour imprimer uniquement l'élément de traitement et le "nom de thread" en cours d'exécution.

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. Exécuter

Charge tout et l'exécute… 10 threads seront lancés pour traiter la plage de données fournie.

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

  }
}

Sortie console

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

Une fois le processus terminé, 10 fichiers CSV seront créés.

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

}

N'oubliez pas, activez l'analyse automatique du composant Spring.

    

6.2 Database partitioner reader - MongoDB example.

job-partitioner.xml

  
    
    
    
    
        
            
        
    
  

Terminé.

Télécharger le code source

Téléchargez-le -SpringBatch-Partitioner-Example.zip (31 Ko)