728x90
안녕하세요
세기무민입니다.
이전 포스팅에서는 Spring Batch 이론에 대해 다뤄봤다면
이번 프로젝트에서는 실제 코드를 작성해보려고 합니다.
👇이전 포스팅은 아래 링크로👇
1. 개발 환경 및 고려 사항
기술 스택은 아래와 같습니다.
- Spring Boot 2.7.3
- H2 Database
- JPA(native, jpql)
- 초기 프로젝트 생성 시에는 DB Connection은 하지 않고 임의로 코드를 Return하여 Job이 정상 처리되는지 확인
개발 방식은 TaskLet 방식으로 구현
- 대용량 처리의 경우 Chunk 방식이 맞으나 POC의 경우 TaskLet 방식으로 충분하다고 판단
- TaskLet은 하나의 Task 안에 reader/processer/writer 역할을 포함하고 있다고 보면 됨
고려할 사항은 아래와 같습니다.
- Spring Batch를 이용하기 위해서는 수행 관련하여 테이블을 생성해야 한다.
- application.yml 설정 파일
- spring.batch.initialize-schema : always
- 배치 관련 테이블 생성
- spring.batch.initialize-schema : always
- application.yml 설정 파일
2. 개발 상세
2.1 application.yml
spring:
datasource:
url: jdbc:h2:mem:[DB 이름]
driverClassName: org.h2.Driver
username: [DB 사용자 이름]
password: [DB PW]
batch:
job:
names: ${job.name:NONE}
enabled: false
jdbc:
# 인메모리 DB를 사용할 경우 always를 하는것이 좋음
# 인메모리 DB의 경우 휘발성으로 매번 TB를 생성해줘야 함
initialize-schema: always
- 인메모리 DB 설정과 프로젝트 실행 시 메타 테이블 스키마를 항상 체크하여 없으면 생성해주도록 셋팅한다.
- 추후 인메모리 DB를 사용하지 않을 경우 always -> never로 변경하여 처리한다.
2.2 application.java
@SpringBootApplication
@EnableBatchProcessing
@EnableScheduling
public class SpringbatchapiApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbatchapiApplication.class, args);
}
}
- 배치 작업을 위한 기본 구성 설정을 위해 EnableBatchProcessing 어노테이션을 사용합니다.
- Spring에서 Scheduler 기능 활성화를 위해 EnableScheduling 어노테이션을 사용해줍니다.
2.3 Job Class(MemberJobConfiguration.java)
@Slf4j
@RequiredArgsConstructor
@Configuration
public class MemberJobConfiguration {
private final JobBuilderFactory jobBuilderFactory;
private final JobCompletionListener jobCompletionListener;
private final MemberStep memberStep;
@Bean(name = "getMemberJob")
public Job getMemberJob(){
return jobBuilderFactory.get("getMemberJob")
.incrementer(new RunIdIncrementer())
.start(memberStep.getMemberStep1())
.on(ExitStatus.STOPPED.getExitCode())
.to(memberStep.getMemberStep3())
.on("*")
.end()
.from(memberStep.getMemberStep1())
.on("*")
.to(memberStep.getMemberStep2())
.next(memberStep.getMemberStep3())
.on("*")
.end()
.end()
.build();
}
}
- Job을 설정해주는 클래스입니다.
- 해당 클래스에서는 Step을 실행시키고 해당 Step의 실행 결과에 따라 다음 Step에 대해 구성하여 처리가 가능합니다.
- RunIdIncrementer를 이용하여 동일 파라미터를 다시 실행할 수 있도록 옵션을 추가해줍니다.
- runId가 매번 증감함에 따라 해당 파라미터를 계속 이용할 수 있게 됩니다.
- 즉, 해당 Job을 반복하여 사용 가능합니다.
- Scheduler만 사용할 때와 달리 Batch를 사용하게 되면 Step별로 분리하여 처리할 수 있다는 점이 최대 장점입니다.
- on("*")의 경우는 위에 설정한 ExisStatus.STOPPED 상태가 아닌 경우 처리합니다.
- 즉 위의 코드를 해석하자면 1번 Step에서 정상적으로 작동하지 않을 경우(STOPPED) 3번 Step을 처리하고
그렇지 않은 경우(STOPPED 이외) 2번 Step > 3번 Step 순서대로 처리한다.
2.4 Step Class (MemberStep.java)
@Configuration
public class MemberStep {
@Autowired
private StepBuilderFactory stepBuilderFactory;
@Autowired
private MemberTasklet memberTasklet;
@Autowired
private MemberSelectTasklet memberSelectTasklet;
@Bean(name = "getMemberStep1")
public Step getMemberStep1(){
return stepBuilderFactory.get("getMemberStep1")
.tasklet(memberTasklet)
.build();
}
@Bean(name = "getMemberStep2")
public Step getMemberStep2(){
return stepBuilderFactory.get("getMemberStep2")
.tasklet(memberTasklet)
.build();
}
@Bean(name = "getMemberStep3")
public Step getMemberStep3(){
return stepBuilderFactory.get("getMemberStep3")
.tasklet(memberTasklet)
.build();
}
}
- 저는 임의로 테스트를 하기 위해 3가지의 Step을 만들었고 TaskLet 방식으로 처리하였습니다.
- 추가로 stepBuilderFactory.get().tasklet 내에 TaskLet 코드를 작성해도 무관하지만
관리 차원에서 TaskLet Class를 분리하여 관리하는 것이 효율적으로 저는 분리하였습니다.
2.5 TaskLet Class(MemberTasklet/MemberTasklet2/MemberTasklet3.java)
/**
* MemberTaskLet 1
*/
@Slf4j
@Configuration
public class MemberTasklet implements Tasklet {
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
log.info("Start GetMember TaskLet");
contribution.setExitStatus(ExitStatus.STOPPED);
log.info("End GetMember TaskLet");
return RepeatStatus.FINISHED;
}
}
/**
* MemberTaskLet 2
*/
@Slf4j
@Configuration
public class MemberTasklet2 implements Tasklet {
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
log.info("Start GetMember TaskLet 2");
log.info("End GetMember TaskLet 2");
return RepeatStatus.FINISHED;
}
}
/**
* MemberTaskLet 3
*/
@Slf4j
@Configuration
public class MemberTasklet3 implements Tasklet {
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
log.info("Start GetMember TaskLet 3");
log.info("End GetMember TaskLet 3");
return RepeatStatus.FINISHED;
}
}
- 저의 경우 임의로 테스트를 위해 Log로 작성하였으나 실제 처리하는 로직을 작성해주면 됩니다.
- MemberTaskLet의 경우 StepContribuition에 ExitStatus 상태 값을 설정해주어
Step에게 해당 TaskLet의 처리 상태를 Return 합니다.- Job에서 Step의 상태로 다음 Step을 결정할 수 있도록 처리하기 위함
2.6 Scheduler Class(MemberScheduler.java)
@Slf4j
@Component
public class MemberScheduler {
@Autowired
private JobRegistry jobRegistry;
@Autowired
private JobLauncher jobLauncher;
public JobLauncher getJobLauncher() {
return jobLauncher;
}
@Scheduled(cron = "0/10 * * * * ?")
public void runJob() {
//job parameter
Map<String, JobParameter> confMap = new HashMap<>();
Calendar calendar = Calendar.getInstance();
Date date = calendar.getTime();
confMap.put("time", new JobParameter(date));
JobParameters jobParameters = new JobParameters(confMap);
try {
jobLauncher.run(jobRegistry.getJob("getMemberJob"), jobParameters);
} catch (Exception e) {
log.error(e.getMessage());
}
}
}
- cron을 이용하여 특정 시간 때 실행되도록 설정해줍니다.
- Scheduler만 사용했을 때는 내부 로직을 작성했다면 Scheduler + Spring Batch 구조를 이용할 때는
JobLauncher를 실행하여 Job이 처리되도록 합니다. - JobLauncher를 실행하기 전 JobParameter를 이용하여 실행되는 Job의 파라미터 값을 추가해줍니다.
- 메타 테이블에 해당 데이터들이 담기게 됩니다.
- 결론적으로 JobLauncher를 실행하여 Scheduler(JobLauncehr) > Job > Step 순으로 처리되게 됩니다.
3. 실행 결과
3.1 MetaTable 확인
- 간단하게 메타 테이블에 어떻게 들어가는지 확인해보도록 하겠습니다.
- 참고로 6개의 모든 테이블을 보는 것이 좋지만 특정 테이블만 봐도 정상적으로 작동하는지 확인 가능합니다.
3.1.1 H2 DB 실행
- H2 DB를 사용할 경우 url을 http://localhost:8080/h2-console로 입력하시면 위와 같은 화면을 볼 수 있습니다.
- 여기서 JDBC URL, UserName, Password는 Application.yml에 작성한 내용을 그대로 입력해주면 됩니다.
- JDBC URL은 이미 등록되어 있을 것입니다.
입력하고 들어오면 메타 테이블 6개가 입력된 것을 확인할 수 있습니다.
3.1.2 Batch Job Execution
- Job Execution TB를 이용하여 Job이 정상적으로 처리되었는지 확인이 가능합니다.
- Status/Exit Code가 Complete일 경우 정상 처리되었다고 볼 수 있습니다.
3.1.3 Batch Job Execution Params
- Scheduler가 실행될 때 설정한 JobParameter의 값이 로그로 남게 됩니다.
- 저의 경우는 Time만 남겨놓은 상태라 해당 Job Execution Id가 언제 실행되었는지 용도로 확인할 수 있습니다.
- 상세한 데이터를 남기고 싶을 경우 JobParameter에 추가로 값을 넣어주면 됩니다.
3.1.4 Batch Step Execution
- Job이 처리될 때 실행한 Step들을 확인할 수 있습니다.
- 즉 내가 설정한 Step대로 실행되었는지 확인도 가능하며 중간에 오류가 발생했는지 분석도 가능합니다.
- Job과 동일하게 Status를 통해 처리 상태를 확인할 수 있습니다.
3.2 Job Step.on 처리
- 우선 Log로 남긴 상태임으로 Stopped과 Stopped이 아닐 때 로그가 어떻게 남는지 확인해보도록 하겠습니다.
3.2.1 Stopped
- Step 1 > Step 3 순서대로 처리됩니다.
3.2.1 Not Stopped
- Step 1 > Step 2 > Step 3 순서대로 처리됩니다.
4. 기타
GitHub 코드를 확인하고 싶을 경우 해당 링크를 클릭해주세요!!!
Spring Batch의 작동에 대해 간단하게 알아볼 수 있었습니다.
다음 포스팅에서는 조금 더 심화 된 내용으로 돌아오겠습니다.
728x90
'Programing > Java & Spring' 카테고리의 다른 글
세무민의 코딩일기 : Spring Batch JobListenerUtils 만들기 (0) | 2023.12.04 |
---|---|
세무민의 코딩일기 : Spring Batch JobParameterVaildator (0) | 2023.11.28 |
세무민의 코딩일기 : Spring Batch API 만들기 1탄(Spring Batch, Scheduler, TaskLet, Chunk등 이론 정리) (0) | 2023.10.29 |
[Spring] Scheduling LockProvider 사용 목적과 사용 시 고려할 사항 정리 (0) | 2023.03.21 |
Java 예외(Exception) 처리 이론 정리 (0) | 2022.10.19 |