web-dev-qa-db-fra.com

Spring batch - exécution de plusieurs travaux en parallèle

Je suis nouveau dans le lot de printemps et je ne savais pas comment faire cela ..

Fondamentalement, j'ai un poller de fichier de printemps qui s'exécute toutes les N minutes pour rechercher des fichiers avec un nom (ex: A.txt & B.txt) dans un certain répertoire. À tout moment, il pourrait y avoir au maximum 2 fichiers dans ce répertoire (A et B). Grâce à Spring Batch Job, ces deux fichiers seront traités et conservés dans 2 tables de base de données différentes.

Ces fichiers sont quelque peu similaires, donc le même processeur/graveur est utilisé.

À l'heure actuelle, comme je l'ai configuré, chaque fichier du cycle d'interrogation 1 est récupéré et le travail est exécuté.

Disons qu'il y a 2 fichiers dans le répertoire (A.txt et B.txt), existe-t-il un moyen de créer 2 jobs pour que les deux jobs puissent être exécutés en parallèle?

6
ljustin

Je crois que vous le pouvez. Puisque vous êtes nouveau dans le lot de printemps (comme moi), je vous recommande de passer par la langue du domaine d'un lot si vous ne l'avez pas déjà fait.

Ensuite, vous pouvez commencer par configurer votre propre asynchroneJobLauncher. Par exemple:

  @Bean
  public JobLauncher jobLauncher() throws Exception
  {
    SimpleJobLauncher jobLauncher = new SimpleJobLauncher();

    jobLauncher.setJobRepository(jobRepository);
    jobLauncher.setTaskExecutor(new SimpleAsyncTaskExecutor());
    jobLauncher.afterPropertiesSet();

    return jobLauncher;
  }

Portez une attention particulière à SimpleAsyncTaskExecutor (le référentiel de tâches peut être câblé automatiquement). Cette configuration permettra asynchrone l'exécution comme visualisé ensuite:

Asynchronous

Comparez-le avec le flux d'exécution synchrone:

enter image description here

Il serait peut-être également utile de citer le SimpleJobLauncher Java doc:

Implémentation simple de l'interface JobLauncher. L'interface Spring Core TaskExecutor est utilisée pour lancer un Job. Cela signifie que le type d'ensemble d'exécuteurs est très important. Si un SyncTaskExecutor est utilisé, le travail sera traité dans le même thread qui a appelé le lanceur. Il convient de veiller à ce que tous les utilisateurs de cette classe comprennent parfaitement si l'implémentation de TaskExecutor utilisée démarrera les tâches de manière synchrone ou asynchrone. Le paramètre par défaut utilise un exécuteur de tâches synchrone.

Plus de détails et d'options de configuration - ici .

À la fin, créez simplement les travaux avec différents noms et/ou lancez-les avec un jeu de paramètres différent. Un exemple naïf serait:

  @Autowired
  public JobBuilderFactory jobBuilderFactory;

  public Job createJobA() {
    return jobBuilderFactory.get("A.txt")
                            .incrementer(new RunIdIncrementer())
                            .flow(step1())
                            .next(step2())
                            .end()
                            .build();
  }

  public Job createJobB() {
    return jobBuilderFactory.get("B.txt")
                            .incrementer(new RunIdIncrementer())
                            .flow(step1())
                            .next(step2())
                            .end()
                            .build();
  }

L'exécution de ces travaux avec votre lanceur de travaux asynchrone créera deux instances de travaux qui s'exécuteront en parallèle. Ceci est juste une option, qui peut ou non convenir à votre contexte.

7
Lachezar Balev

Il existe de très bonnes approches pour exécuter des jobs en mode asynchrone avec Spring, c'est juste une question de configuration de JobLauncher. JobLauncher possède une propriété taskExecutor et l'exécution asynchrone peut être activée en fonction de l'implémentation affectée à cette propriété.

Vous pouvez trouver tous les types TaskExecutor que Spring peut fournir et en fonction de vos besoins, sélectionnez la meilleure approche pour accomplir vos tâches asynchrones par lots. Types d'exécuteurs de tâches au printemps

Par exemple, SimpleAsyncTaskExecutor est un exécuteur de tâches qui créera un nouveau Thread à chaque appel et qui pourrait générer un problème de performances si l'exécution s'exécute à haute fréquence. D'autre part, il existe également des types TaskExecutors qui fournissent des fonctionnalités de mise en commun afin de réutiliser les ressources et de maximiser l'efficacité du système.

Voici un petit exemple de la façon de configurer un ThreadPoolTaskExecutor:

A) Configurer le bean ThreadPoolTaskExecutor

@Bean
    public ThreadPoolTaskExecutor taskExecutor() {
    ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
    taskExecutor.setCorePoolSize(15);
    taskExecutor.setMaxPoolSize(20);
    taskExecutor.setQueueCapacity(30);
    return taskExecutor;
}

B) Configurer le JobLauncher Bean

   @Bean
    public JobLauncher jobLauncher(ThreadPoolTaskExecutor taskExecutor, JobRepository jobRepository){
        SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
        jobLauncher.setTaskExecutor(taskExecutor);
        jobLauncher.setJobRepository(jobRepository);
        return jobLauncher;
    }

C) Injectez votre configuration JobLauncher et Jobs

@Autowired
private JobLauncher jobLauncher;

@Autowired
@Qualifier("job1-file-A")
private Job job1;

@Autowired
@Qualifier("job2-file-B")
private Job job2;

D) Planifier les travaux

@Scheduled(cron = "*/1 * * * * *")
public void run1(){
    Map<String, JobParameter> confMap = new HashMap<>();
    confMap.put("time", new JobParameter(System.currentTimeMillis()));
    JobParameters jobParameters = new JobParameters(confMap);
    try {
        jobLauncher.run(job1, jobParameters);
    }catch (Exception ex){
        logger.error(ex.getMessage());
    }

}

@Scheduled(cron = "*/1 * * * * *")
public void run2(){
    Map<String, JobParameter> confMap = new HashMap<>();
    confMap.put("time", new JobParameter(System.currentTimeMillis()));
    JobParameters jobParameters = new JobParameters(confMap);
    try {
        jobLauncher.run(job2, jobParameters);
    }catch (Exception ex){
        logger.error(ex.getMessage());
    }

}

E) Enfin sur votre classe SpringBoot @EnableBatchProcessing et @EnableScheduling

@EnableBatchProcessing
@EnableScheduling
@SpringBootApplication
public class MyBatchApp {
11
Daniel C.