====== 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]]