====== SpringBoot와 SpringBatch ====== * [[springframework:springboot|SpringBoot]]와 [[springframework:batch|Spring Framework Batch]] * Spring Boot 에서는 [[springframework:batch:commandlinejobrunner|Spring Batch Command Line Job Runner]] 를 사용할 수 없다. Spring Boot 의 Auto Configuration이 작동하지 않게 된다. 아래는 SpringBoot 에서 명령행(Command line)으로 Spring Batch Job을 실행하는 방법이다. * [[https://github.com/kwon37xi/springboot2-batch-example|SpringBoot2 Batch Example]] 예제 소스 ===== 의존성 및 bootJar 설정 ===== compile('org.springframework.boot:spring-boot-starter-batch') // 2.x bootJar { mainClassName = "my.boot.batch.BatchApplication" } // 1.x - 지정하지 않으면 자동으로 @SpringBootApplication 애노테이션이 붙은 클래스로 자동 지정됨. bootRepackage { mainClass = "my.boot.batch.BatchApplication" } ===== BatchApplication 구성 ===== * starter 의 ''org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration'' 와 ''org.springframework.boot.autoconfigure.batch.BasicBatchConfigurer'' 를 분석해보면 작동 원리를 알 수 있다. * 올바르게 Boot Auto Configuration 을 사용하려면 [[https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/autoconfigure/batch/JobLauncherCommandLineRunner.html|JobLauncherCommandLineRunner]] Bean을 생성해줘야 한다. * ''JobLauncherCommandLineRunner''는 * **''spring.batch.job.enabled=true''**일 때 자동 생성되며, ''spring.batch.job.names'' 에 쉼표로 구분되어 지정된 Job들을 실행한다. 단, **''spring.batch.job.names''가 존재하지 않으면 모든 Job을 실행해버린다!** * ''@EnableBatchProcessing''에 의해 생성된 ''JobLauncher'', ''JobExplorer''를 주입받고, * **Program 인자로 ''param1=value1 param2=value2''로 준 값이 자동으로 Job Parameter key/value로** 들어가게 된다. * 기본적으로 ''@Primary'' ''DataSource''를 자동으로 사용한다. 보통 이 경우 문제가 된다. 별도 DB를 따로 사용하게 설정하려면 [[springframework:batch|Spring Framework Batch]] 에서 설정 Override 참조. @Slf4j @EnableBatchProcessing @SpringBootApplication @Import(각종기타 설정 import) public class BatchApplication extends DefaultBatchConfigurer { public static void main(String[] args) { int exitCode = SpringApplication.exit(SpringApplication.run(BatchApplication.class, args)); System.exit(exitCode); } @Value("${spring.batch.job.names:NONE}") private String jobNames; // 어디선가 미리 생성해둔 spring batch 용 database @Autowired @Override public void setDataSource(@Qualifier("batchDataSource") DataSource batchDataSource) { super.setDataSource(batchDataSource); } // spring.batch.job.names 를 지정하지 않으면 모든 Job이 실행돼 버리기 때문에 // 방어차원에서 넣은 job.names validation 처리 @PostConstruct public void validateJobNames() { log.info("jobNames : {}", jobNames); if (jobNames.isEmpty() || jobNames.equals("NONE")) { throw new IllegalStateException("spring.batch.job.names=job1,job2 형태로 실행을 원하는 Job을 명시해야만 합니다!"); } } } > ''DefaultBatchConfigurer'' 사용시에 이 클래스내에서 ''transactionManager''를 batchDataSource 용으로 생성해버려서 원래 애플리케> 이션의 > DataSourceTransactionManagerAutoConfiguration.DataSourceTransactionManagerConfiguration 가 작동하지 않게 된다. > 따라서 항상 **애플리케이션용 DataSource에 대한 transactionManager를 항상 명시적으로 만들어주고 @Primary 설정**을 해줘야 한다. ===== 예제 Batch Job Configuration ===== @Slf4j @Configuration // 아래를 통해 실행 대상 job으로 들어갔을 때만 해당 Job 설정이 수행되게 하면 불필요한 설정 부담을 줄일 수 있고 // 실수로 job이 실행되는 것을 막을 수 있다. @ConditionalOnProperty(name = "spring.batch.job.names", havingValue = "helloJob") public class HelloBatchConfiguration { @Autowired private JobBuilderFactory jobBuilderFactory; @Autowired private StepBuilderFactory stepBuilderFactory; public HelloBatchConfiguration() { // job.names가 helloJob 일 때만 생성자의 아래 로그가 출력됨. log.info("======== Hello Job configured!! =========="); } @Bean(name = "helloJob") public Job helloJob() { return jobBuilderFactory.get("helloJob") .start(step()) .build(); } @Bean public Step step() { return stepBuilderFactory.get("taskletStep").tasklet(new Tasklet() { @Override public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception { log.info("==== Tasklet called...."); Map jobParameters = chunkContext.getStepContext().getJobParameters(); // 모든 Job Parameter 출력 jobParameters.forEach((key, value) -> { log.info("key : {}, value : {}", key, value); }); return RepeatStatus.FINISHED; } }).build(); } } ===== application.properties/yml ===== * ''spring.batch.job.enabled=true'' 여야 ''JobLauncherCommandLineRunner''가 활성화 된다. spring: batch: job: enabled: true initialize-schema: embedded main: web-application-type: NONE * ''spring.batch.initialize-schema'' 는 거의 항상 ''embdded''로 지정하며, 별도 DB 사용시에는 ''never''로 지정한다. ''always''인 경우는 거의 없다고 봄. * SpringBoot 2.x 기준, ''spring-boot-starter-web'', ''spring-boot-starter-webflux''가 의존성에 없어야 하며, 만약 있다면 ''spring.main.web-application-type=NONE''으로 설정할 것. * Spring Batch 를 integraition test 할 때 ''spring.batch.job.enabled=false'' 로 두고 ''JobLauncherTestUtils'' 로 런칭하게 하지 않으면 배치 Job이 두 번 실행된다. 첫번째 실행시 파라미터가 잘못 주입된다. ===== 실행 ===== ''bootJar''로 jar를 생성하고 gradle bootJar java -jar your-boot.jar --spring.batch.job.names=${JOB이름} \ JobParam1=ParamValue1 JobParam2=ParamValue2 * **''spring.batch.job.names''는 쉼표로 구분하여 여러개도 지정 가능하다.** * **''spring.batch.job.names''를 명시하지 않으면 모든 job 이 다 실행된다.** ===== 완전 auto configuration ===== * 완전히 AutoConfig 로 SpringBoot batch 를 설정할 때 ''@org.springframework.boot.autoconfigure.batch.BatchDataSource'' 를 사용하여 batch 용 dataSource 지정이 가능하다. ==== spring-boot-starter-batch-web ==== * [[https://github.com/codecentric/spring-boot-starter-batch-web|codecentric/spring-boot-starter-batch-web]] * SpringBoot Web으로 batch Job 실행. ===== 참고 ===== * [[https://cheese10yun.github.io/spring-batch-basic/|Spring Batch 간단 정리 - Yun Blog | 기술 블로그]] * [[https://spring.io/blog/2021/01/27/spring-batch-on-kubernetes-efficient-batch-processing-at-scale|Spring Batch on Kubernetes: Efficient batch processing at scale]]