스프링 배치에서 Flow 를 사용하기 위해서는 FlowJob
을 생성해야한다.
위 사진은 Flow 생성과 관련된 아키텍처와 플로우를 그림으로 정리했다.
Flow 는 말 그대로 실행 흐름 전환
을 목표로 하기에, 이를 위한 Transition
을 잘 이해하는 것이 중요하다.
FlowJob 을 만들기 위해서는 우선 JobFlowBuilder
를 만들고,
.end().build()
를 통해 FlowJob 을 생성할 수 있다.
JobFlowBuilder 는 위 그림과 같이 SimpleJobBuilder
, FlowJobBuilder
모두를 통해 생성이 가능하다.
- Step의 실행 흐름을 "조건에 따라 전환" 을 정의한다.
- on (pattern: String) 메소드를 통해 TransitionBuilder 를 생성하고, Transition Flow 를 구성한다.
- Step의 실행 결과 (ExitStatus) 를 기반으로 on (pattern: String) 을 정의하며, step 의 실행 결과 (ExitStatus) 가 on (pattern: String) 의 어떠한 패턴과도 매칭되지 않으면 예외를 발생한다.
- 구체적인 것부터, 구체적이지 않은 순서대로 적용된다.
분기를 위한 첫 단계. 분기를 위한 패턴(조건식)을 설정한다.
JobFlowBuilder 에서 유일하게 TransitionBuilder
를 생성한다.
Step의 실행 결과인 ExitStatus 와 매칭된다. (BatchStatus 가 아니니 주의)
특수문자는 아래 2개만 지원한다.
*
: 0개 이상의 아무 임의의 문자(열)과 매칭.?
: 1개의 아무 임의의 문자와 매칭.예를 들어, 문자열이 apple 인 경우 pattern 에
apple
, apple*
, *apple
, app*le
, app*
, *e
, *
-> 매칭됨
apple
, app??
, ap?le
, ?pple
, ?????
-> 매칭됨
appl
, a*l
, ?le
, apple?
, ?
-> 매칭 안됨
on절에 매칭이 됐을 때, 다음으로 실행할 단계를 정의
이전에 정의한 Step, Flow, JobExecutionDecider 로부터 새로운 분기를 추가할 때 사용.
이전에 정의된 적이 없는 경우, 신규로 등록한다.
(FlowBuilder.java 중)
private void doFrom(Object input) {
if (this.currentState == null) {
this.doStart(input);
}
...
}
FlowExecutionStatus 가 STOPPED
상태로 종료되는 transition
Job 의 BatchStatus 와 ExitStatus 가 STOPPED
으로 종료됨
FlowExecutionStatus 가 FAILED
상태로 종료되는 transition
Job 의 BatchStatus 와 ExitStatus 가 FAILED
으로 종료됨
FlowExecutionStatus 가 COMPLETED
상태로 종료 되는 transition
Job 의 BatchStatus 와 ExitStatus 가 COMPLETED
으로 종료됨
Step 의 ExitStatus 가 FAILED 이더라도 Job 의 BatchStatus 가 COMPLETED 로 종료하도록 가능하며 이 때 Job의 재시작은 불가능함
stop() 과 기본적으로 흐름은 동일하다.
(= FlowExecutionStatus 가 STOPPED 상태로 종료되며, Job 의 BatchStatus 와 ExitStatus 가 STOPPED 상태로 종료된다.)
차이점이라면, 다음에 Job 을 재시작 할 때 여기에 명시된 Step / Flow / JobExecutionDecider 부터 시작되도록 강제된다.
@Bean
public Job batchJob() {
return jobBuilderFactory.get(“batchJob")
.start(Step1 / Flow1) // 최초에 실행할 Step / Flow 지정
.on(pattern: String) // Step, Flow 의 ExitStatus 와 패턴 비교
.to(Step2 / Flow2) // 위 on절과 패턴이 일치할 경우 실행할 Step / Flow
.stop() / fail() / end() / stopAndRestart() // 위에 to 또는 이런것들도 쓸 수 있다.
.from(Step1) // 위에 정의한 Step1 로부터
.next(Step3) // 그 다음에는 Step3 을 실행한다.
.end() // SimpleFlow 를 생성하고, SimpleFlow 를 래핑한 FlowJobBuilder 반환
.build() // FlowJob 반환
}
@Bean
public Job batchJob() {
return jobBuilderFactory.get(“batchJob")
.start(step1) // 최초에 step1 을 실행한다.
.on("COMPLETED") // ExitStatus 가 COMPLETED 이면
.to(step2) // step2 를 실행한다.
.from(step1) // step1 로부터
.on("*") // 모든 패턴 (COMPLETED 를 위에서 걸렀기 때문에, else 가 된다.)
.to(step3) // step3 을 실행한다.
.end() // SimpleFlow 를 생성하고, SimpleFlow 를 래핑한 FlowJobBuilder 반환
.build() // FlowJob 반환
}
@Bean
public Job batchJob() {
return jobBuilderFactory.get(“batchJob")
.start(step1) // 최초에 step1 을 실행한다.
.on("COMPLETED") // ExitStatus 가 COMPLETED 이면
.to(step2) // step2 를 실행한다.
.on("*") // (1)... step2 의 실행 결과와 상관없이
.stop() // Stop 상태로 종료
.from(step1) // step1 로부터
.on("*") // 모든 패턴 (COMPLETED 를 위에서 걸렀기 때문에, else 가 된다.)
.to(step3) // step3 을 실행한다.
.next(step4) // (2)... 이후 step4 를 실행한다.
.on("*") // step4 의 실행 결과와 상관없이
.end() // End 상태로 종료
.end() // SimpleFlow 를 생성하고, SimpleFlow 를 래핑한 FlowJobBuilder 반환
.build() // FlowJob 반환
}
주석에 보면 (1) 과 (2) 로 달아둔 부분이 있다.
결과와 상관 없이 다음 스텝을 호출
하는 부분인데, on("*") 을 사용하기도, 안하기도 해서 관련 내용을 적어보기로 한다.
(1) 을 보면, step2 의 결과와 상관없이 stop() 하는데 on("*")
을 사용했으며,
(2) 를 보면, step3 의 결과와 상관없이 step4를 호출하는데, on("*")
을 사용하지 않았다.
이는 위에 아키텍처 사진을 다시 보면 답을 알 수 있다.
(1) 에서 to, stop, fail, end, stopAndRestart 는 Transition 이기 때문에 TransitionBuilder 를 필요로 한다.
따라서 on 을 통해 TransitionBuilder 를 생성했다.
(2) 에서 next() 는 flow 로 다음에 실행할 흐름을 지정하기 때문에, FlowBuilder 에서 처리된다.
이 또한 위에 아키텍처 사진을 보면 to 의 반환값이 FlowBuilder 임을 알 수 있다.