관리 메뉴

나만의공간

Spring Batch #8 (Job의 이해) 본문

IT/Spring Batch

Spring Batch #8 (Job의 이해)

밥알이 2023. 10. 11. 09:52

JobLauncherApplicationRunner

  • Spring Batch 작업을 시작하는 ApplicationRunner로서 BatchAutoConfiguration에서 생성
  • 스프링 부트에서 제공하는 ApplicationRunner의 구현체로 어플리케이션이 정상적으로 구동되자 마자 실행됨
  • 기본적으로 Bean으로 등록된 모든 Job을 실행 시킵니다.

BatchProperties

  • application.yml 에서 Spring Batch 의 환경 설정을 할 수 있음
spring:
  batch:
    job:
      enabled: true 
      names: ${job.name:NONE} 
    jdbc:
      initialize-schema: always
      table-prefix: SYSTEM_
  • enabled
    • 기본값은 true
    • false는 스프랑이 자동으로 job을 실행하는 것을 막습니다.
  • names
    • enabled이 true일 때, 자동으로 전체 Job이 실행되는데 특정 Job만 실행하도록 하는 옵션입니다.
    • 여러개 작성시 쉼표(,)로 구분합니다.
    • 하드코딩으로 넣어도 되지만 위처럼 사용하면 실행 시 argument로 주입받아서 사용할 수 있습니다.
      1. --job.name=job1,job2
      2. job1,job2만 실행하고 존재하지 않으면 실행하지 않습니다.
      3. 만약 아무런  argument가 주입되지 않는다면 아무런 배치도 실행되지 않습니다.
  • initialize-schema
    • EMBEDED: 내장 DB일 때만 실행되며 스키마가 자동으로 생성(Default)
    • ALWAYS: 스키마 자동생성 Script 항상 실행
    • NEVER
      1. 스크립트 항상 실행 안하기 때문에 내장DB일 경우 스키마 미생성으로 오류 발생
      2. 운영에서는 수동으로 스크립트 생성하는 것을 권장함.
  • table-prefix
    • Spring Batch Meta Table 생성시 테이블 앞의 문자 변경 옵션
    • 기본값은 BATCH_ 임

JobBuilderFactory, JobBuildr 란

스프링 배치는 Job을 쉽게 생성 및 설정할 수 있도록 Util 성격의 빌더 클래스들을 제공합니다.

  • JobBuilderFactory
    • JobBuilder를 생성하는 팩토리 클래스로 get 메서드를 제공합니다.
    • JobBuilderFactory.get("jobName")
      1. 내부적으로 JobBuildr가 JobName을 잡의 이름으로 하여 잡을 생성하도록 로직이 구성되어 있습니다.
  • JobBuilder
    • Job을 구성하는 설정 조건에 따라 두 개의 하위 빌더 클래스를 생성하여 직접 Job을 생성하지 않고 Job 생성을 위임합니다.
    • SimpleJobBuilder
      1. SimpleJob을 생성하는 Builder 클래스
      2. Job 실행과 관련된 여러 설정 API 제공
    • FlowJobBuilder
      1. FlowJob을 생성하는 Builder 클래스
      2. 내부적으로 FlowBuilder를 반환함으로써 Flow 실행과 관련된 여러 설정 API를 제공

  • JobBuilderFactory에서 get메소드를 통해 JobBuilder 클래스를 생성
  • JobBuilder에서 start(step)를 사용하면 JobBuilder 내부적으로 SimpleJobBuilder를 생성하여 SimpleJob을 생성합니다.
  • JobBuilder에서 start(flow) 또는 flow(step)을 사용하면 JobBuilder 내부적으로 FlowJobBuilder를 생성하여 FlowJob을 생성합니다.
    FlowJobBuilder는 내부적으로 JobFlowBuilder 클래스를 생성하여 Flow를 생성합니다.
  • SimpleJobBuilder와 JobFlowBuilder는 위 그림과 같이 다양한 API를 제공합니다.

상속구조

  • JobBuilderFactory는 JobBuilder 클래스를 생성하는 역활을 함
  • JobBuilder 클래스는 JobBuilderHelper를 상속받음.
  • JobBuilderHelper 클래스는 내부 static 클래스로 CommonJobProperties를 갖고 있으며, 이를 필드값으로 Job을 생성하는 공통적인 내용을 갖고 있다고 보면 됩니다.
  • JobFactory에서 JobBuilder를 생성할 때 생성자의 인자로 JobRepository를 넘기는데 이게 JobBuilder에 CommonJobProperties에 담기게 됩니다.
  • JobBuilder가 생성하는 SimpleJobBuilder와 FlowJobBuilder는 JobBuilderHelper를 상속받고 있음
  • SimpleJobBuilder와 FlowJobBuilder는 각각 Job을 생성하게 되고, JobRepository는 빌더 클래스를 통해 Job 객체에 전달되어 메타데이터를 기록하는데 사용됩니다.

SimpleJob

  • SimpleJob은 Step을 실행시키는 Job 구현체로서 SimpleJobBuilder에 의해 생성된다.
  • 여러 단계의 Step으로 구성할 수 있으며, Step을 순차적으로 실행 시킵니다.
  • 모든 Step의 실행이 성공적으로 완료되어야 Job이 성공적으로 완료 됩니다.
  • 맨 마지막에 실행한 Step의 BatchStatus가 Job의 최종 BatchStatus가 됩니다.

기본API

validator()

  • Job 실행에 꼭 필요한 파라미터를 검증하는 용도
  • DefaultJobParameterValidator 구현체를 지원함.

동작과정

예제

Validator를 실행하는 배치Job

package com.study.springbatch.domain.job;

import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobParameter;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersInvalidException;
import org.springframework.batch.core.JobParametersValidator;
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.core.job.DefaultJobParametersValidator;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * 사용자 Parameter Validtion 학습
 */
@Slf4j
@Configuration
@RequiredArgsConstructor
public class Study8JobConfig {

	private final JobBuilderFactory jobBuilderFactory;
	private final StepBuilderFactory stepBuilderFactory;

	@Bean
	public Job study8() {
		return jobBuilderFactory.get("study8")
			.start(study8Step1())
			//사용자 정의 Validator 사용법
			.validator(new CustomerParameterValidator())
			// 기본 Validation 사용방법
			// .validator(new DefaultJobParametersValidator(new String[] {"name","date"}, new String[]{"count"} ))
			.build();
	}

	@Bean
	public Step study8Step1() {
		return stepBuilderFactory.get("study8Step1")
			.tasklet((stepContribution, chunkContext) -> {
				System.out.println("study8Step1 executed");
				return RepeatStatus.FINISHED;
			})
			.build();
	}

}

사용자정의 Validator 클래스

package com.study.springbatch.domain.job;

import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersInvalidException;
import org.springframework.batch.core.JobParametersValidator;

public class CustomerParameterValidator implements JobParametersValidator {
	@Override
	public void validate(JobParameters parameters) throws JobParametersInvalidException {
		// name값이 "aaaa"가 아니면 오류 발생
		if (!parameters.getString("name").equals("aaaa")) {
			throw new JobParametersInvalidException("name 값이 올바른 값이 아닙니다.");
		}
	}
}

preventRestart()

package com.study.springbatch.domain.job;

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;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * Job 메소드 중 preventRestart 테스트
 * preventRestart를 호출하면 false인 상태로 동작 합니다.
 * preventRestart를 미호출 하면 true인 상태로 동작 합니다.
 *
 * preventRestart
 * -. 기본적으로 Job이 실패할경우 재시작이 가능한데 해당값을 false로 주게 되면
 * -. 재시작을 지원하지 않습니다.
 */
@RequiredArgsConstructor
@Slf4j
@Configuration
public class PreventRestartJobConfig {

	private final JobBuilderFactory jobBuilderFactory;
	private final StepBuilderFactory stepBuilderFactory;

	@Bean
	public Job preventRestartJob() {
		return jobBuilderFactory.get("preventRestartJob")
			.start(preventRestartStep())
			/**
			 * preventRestart 호출 하면 false로 세팅
			 * preventRestart 미호출 하면 true로 세팅
			 */
			// .preventRestart()
			.build();
	}

	@Bean
	public Step preventRestartStep() {
		return stepBuilderFactory.get("preventRestartStep")
			.tasklet((stepContribution, chunkContext) -> {
				log.info("preventRestartStep 테스트");
				return RepeatStatus.FINISHED;
			})
			.build();
	}
}

incrementer()

  • JobParameters로 Job이 성공했다면 기본적으로 동일한 JobParameters로 동일한 Job을 실행시킬 수 없음.
  • 하지만 같은 JobParameters로 계속 Job을 실행해야 할 때가 있음.
  • 이떄 JobIncrementer를 사용합니다.
  • 즉, 기존의 JobParameter 변경없이 Job을 여러 번 시작하고자 할 때 사용 합니다.
  • Incrementer는 특별한 일을 하는것이 아니라 실행되기 위해 주입된 파라미터에 추가로 파라미터를 더해주는 역활만 함.
  • 구현체로 지원하는 RunIdIncrementer()의 경우 run.id라는 키로 실행될 때마다 1부터 증가함.
package com.study.springbatch.domain.job;

import java.text.SimpleDateFormat;
import java.util.Date;

import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.JobParametersIncrementer;

public class CustomerJobParametersIncrementor implements JobParametersIncrementer {

	public static final SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd-hh");

	@Override
	public JobParameters getNext(JobParameters parameters) {
		String id = sdf.format(new Date());
		return new JobParametersBuilder().addString("customer",id).toJobParameters();
	}
}
package com.study.springbatch.domain.job;

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.core.launch.support.RunIdIncrementer;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * incrementer 기능 테스트
 *
 */
@RequiredArgsConstructor
@Slf4j
@Configuration
public class IncrementJobConfig {

	private final JobBuilderFactory jobBuilderFactory;
	private final StepBuilderFactory stepBuilderFactory;

	@Bean
	public Job incrementJob() {
		return jobBuilderFactory.get("incrementJob")
			.start(incrementStep())
			//사용자 정의 패턴
			// .incrementer(new CustomerJobParametersIncrementor())
			//기본 패턴
			.incrementer(new RunIdIncrementer())
			.build();
	}

	@Bean
	public Step incrementStep() {
		return stepBuilderFactory.get("incrementStep")
			.tasklet((stepContribution, chunkContext) -> {
				log.info("확인 Job 기능: increment");
				return RepeatStatus.FINISHED;
			})
			.build();
	}

}

SimpleJob 흐름도

  • JobLauncher는 SimpleJob과 JobParameters를 갖고 Job을 실행 시킴
  • Job이 실행될 때 필요한 메타 데이터들을 생성함
  • Job이 실행되기 전에 JobListener에서 beforeJob이 호출
  • 각 Step이 실행되면서 StepExecution과 ExecutionContext가 생성됨.
  • Step이 종료되면 JobListener에서 afterJob이 호출
  • 마지막 Step 단계의 BatchStatus와 ExitStatus를 Job에 업데이트시키고 끝

참고
https://velog.io/@backtony/Spring-Batch-Job

Comments