기존 Config 설정 기반의 추가 설정 하기

Spring Batch의 설정 과정과 실행 방법을 배우게 됩니다.

요약

  • Spring Batch의 기본 구성 요소에 대한 설정 방법을 설명합니다.
  • JobRepository, JobLauncher 등을 등록하는 방법을 배우게 됩니다.
  • 배치 Job 및 Step을 정의하고 실행하는 다양한 방법을 배운다.

배경/문제

Spring Batch를 사용하기 위해서는 배치 메타테이블을 관리할 JobRepository, JobLauncher 등을 빈으로 등록해야 합니다. 이에 따라 Job과 Step을 정의하고, 원하는 시점에 배치 Job을 실행하는 코드가 필요합니다.

접근/해결 전략

구성 과정은 크게 세 가지 단계로 나눌 수 있습니다.

  1. Spring Batch 인프라 설정
  2. Job 및 Step 정의
  3. Job 실행 로직 구현

구현 포인트

1) 배치 메타 설정 (Spring Batch 인프라 설정)

아래 예시는 batchDataSource를 사용하고, Spring Batch에서 요구하는 JobRepository, JobLauncher 등을 등록하는 방법입니다.

package syworks.techlab.base.config.batch;

import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.repository.support.JobRepositoryFactoryBean;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager;

import javax.sql.DataSource;

@Configuration
@EnableBatchProcessing
public class BatchMetaConfig {

    @Bean
    public JobRepository jobRepository(
        @Qualifier("batchDataSource") DataSource batchDataSource,
        @Qualifier("batchTransactionManager") PlatformTransactionManager batchTxManager
    ) throws Exception {
        JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
        factory.setDataSource(batchDataSource);
        factory.setTransactionManager(batchTxManager);
        factory.setIsolationLevelForCreate("ISOLATION_READ_COMMITTED");
        factory.setTablePrefix("BATCH_");
        factory.afterPropertiesSet();
        return factory.getObject();
    }

    @Bean
    public JobBuilderFactory jobBuilderFactory(JobRepository jobRepository) {
        return new JobBuilderFactory(jobRepository);
    }

    @Bean
    public StepBuilderFactory stepBuilderFactory(
        JobRepository jobRepository,
        @Qualifier("batchTransactionManager") PlatformTransactionManager batchTxManager
    ) {
        return new StepBuilderFactory(jobRepository, batchTxManager);
    }
}

@EnableBatchProcessing를 사용하면 기본적으로 JobLauncher, JobRepository 등이 자동으로 등록됩니다. 다중 DataSource 환경에서는 수동 구성을 하는 경우가 많습니다.

2) 실제 배치 Job/Step 정의

위에서 만든 JobBuilderFactory, StepBuilderFactory를 사용하여 Job과 Step을 정의합니다.

package syworks.techlab.base.config.batch;

import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ExampleJobConfig {

    private final JobBuilderFactory jobBuilderFactory;
    private final StepBuilderFactory stepBuilderFactory;

    public ExampleJobConfig(JobBuilderFactory jobBuilderFactory,
                            StepBuilderFactory stepBuilderFactory) {
        this.jobBuilderFactory = jobBuilderFactory;
        this.stepBuilderFactory = stepBuilderFactory;
    }

    @Bean
    public Job exampleJob() {
        return jobBuilderFactory.get("exampleJob")
            .start(exampleStep())
            .build();
    }

    @Bean
    public Step exampleStep() {
        return stepBuilderFactory.get("exampleStep")
            .tasklet((contribution, chunkContext) -> {
                System.out.println("Running exampleStep with batchDataSource...");
                return RepeatStatus.FINISHED;
            })
            .build();
    }
}

Tasklet을 사용한 단순 예시입니다. Chunk-Oriented 로직도 동일한 원리로 작성 가능합니다.

3) 배치를 실제로 실행하는 방법

배치 Job 실행 시점 및 방법은 운영 정책에 따라 다릅니다.

(a) 애플리케이션 기동 시 자동 실행

CommandLineRunnerApplicationRunner를 사용해 앱 시작 시 배치 Job을 한 번 수행합니다.

package syworks.techlab.base.batch.runner;

import org.springframework.batch.core.Job;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.launch.JobExecutionNotRunningException;
import org.springframework.batch.core.launch.JobParametersBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

@Component
public class BatchJobRunner implements CommandLineRunner {

    @Autowired
    private JobLauncher jobLauncher;

    @Autowired
    private Job exampleJob;

    @Override
    public void run(String... args) throws Exception {
        try {
            var jobParams = new JobParametersBuilder()
                .addLong("time", System.currentTimeMillis())
                .toJobParameters();
            jobLauncher.run(exampleJob, jobParams);
        } catch (JobExecutionNotRunningException e) {
            e.printStackTrace();
        }
    }
}

(b) 스케줄링

@Scheduled를 사용하여 특정 주기로 Job을 실행합니다.

package syworks.techlab.base.batch.runner;

import org.springframework.batch.core.Job;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;

@Configuration
@EnableScheduling
public class BatchScheduler {

    @Autowired
    private JobLauncher jobLauncher;

    @Autowired
    private Job exampleJob;

    @Scheduled(cron = "0 0/5 * * * *")
    public void runJob() throws Exception {
        var jobParams = new JobParametersBuilder()
            .addLong("time", System.currentTimeMillis())
            .toJobParameters();
        jobLauncher.run(exampleJob, jobParams);
    }
}

(c) REST API

관리자 페이지 같은 곳에서 HTTP 요청을 통해 배치 Job을 트리거할 수 있습니다.

package syworks.techlab.base.batch.api;

import org.springframework.batch.core.Job;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/batch")
public class BatchController {

    private final JobLauncher jobLauncher;
    private final Job exampleJob;

    public BatchController(JobLauncher jobLauncher, Job exampleJob) {
        this.jobLauncher = jobLauncher;
        this.exampleJob = exampleJob;
    }

    @PostMapping("/example")
    public ResponseEntity<String> runExampleJob() {
        try {
            var jobParams = new JobParametersBuilder()
                .addLong("time", System.currentTimeMillis())
                .toJobParameters();
            jobLauncher.run(exampleJob, jobParams);
            return ResponseEntity.ok("exampleJob started");
        } catch (Exception e) {
            return ResponseEntity.badRequest().body("error: " + e.getMessage());
        }
    }
}

주의사항/트레이드오프

  • 배치 Job을 실행할 때 파라미터를 각기 다르게 주어야 중복 실행 오류를 피할 수 있습니다.
  • 단일 @Configuration 클래스 내에서 모든 설정을 처리할 수 있지만, 규모가 커질 경우 유지보수 측면에서 분리하는 것이 유리합니다.

마무리

  1. DB 설정(배치 전용 Config)
    • batchDataSourcebatchTransactionManager 생성.
  2. Spring Batch 인프라 설정
    • @EnableBatchProcessing로 자동 구성 활성화.
    • 다중 DataSource 환경에서는 수동 설정 필요.
  3. Job/Step 정의 -

© 2024. Chiptune93 All rights reserved.