프로그래밍/java, spring

Spring Scheduler, Batch연동

김선국 강사 2022. 12. 31. 21:15
728x90

Spring 스케쥴러와 Batch의 연동을 통해 게시판의 예약 글쓰기 기능을 구현해보고자 한다.

게시판예약글쓰기가 동작하는 것에 대한 전반적인 설명은, Spring Scheduler에 관한 글에서 썼으니 먼저 해당 글을 참고해보길 바란다.

-관련 git source : https://github.com/kimseonguk197/spring_batch_scheduler

앞서, Scheduler를 이용한 방법에서는 한 서버내에서 스케쥴러를 통해 DB조회와 UPDATE를 처리하였다. 그런데, 실제 현업에서는 대용량의 DB를 일괄처리하는 경우가 많고 이를 한 서버내에서 수행할 경우 서버에 큰 부담이 될수가 있다.

그래서, 스케쥴러가 있는 서버를 별도로 띄우고 스케쥴러를 대용량DB처리에 특화돼 있는 batch와 연동시키고자 한다. 쉽게 생각해서, 웹애플리케이션은 별도로 존재하고, 스케쥴러 + 배치 전용 서버를 만들겠다는 것이다.


build.gradle

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-batch'
	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
	implementation 'mysql:mysql-connector-java'
	compileOnly 'org.projectlombok:lombok'
	annotationProcessor 'org.projectlombok:lombok'
}

web라이브러리는 필요 없으니 배제하고, batch, jpa, mysql커넥터, 롬복정도만 있으면 된다.

application.yml

server:
  port: 8082
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3300/board?useSSL=false&allowPublicKeyRetrieval=true
    username: root
    password: test1234
  jpa:
    database: mysql
    database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
    show_sql: true
    format_sql: true
    generate-ddl: true
    hibernate:
      ddl-auto: update
#배치잡 사용시 최초 실행의 경우 false로
  batch:
    job:
      enabled: false

애플리케이션서버와 다른 port로 서버를 기동한다. 또한 DB에 대한 crud권한이 필요하므로, mysql datasource설정과 jpa설정을 해주고, 서버가 실행되자마자 배치잡이 실행되는것을 방지하기 위해 job enabled를 false로 둔다.

Application프로그램

@EnableBatchProcessing
@EnableScheduling
@SpringBootApplication
public class BoardApplication {
	public static void main(String[] args) {
		SpringApplication.run(BoardApplication.class, args);
	}
}

Batch와 스케쥴러를 실행하기 위한 어노테이션을 추가한다.

스케쥴러프로그램

@Slf4j
@Component
public class PostScheduleBatch {
    @Autowired
    private JobLauncher jobLauncher;
    @Autowired
    private PostJobConfiguration postJobConfiguration;
    private final PostRepository repository;
    public PostScheduleBatch(PostRepository repository) {
        this.repository = repository;
    }
    @Scheduled(cron =  "0 0/1 * * * *")
    public void postSchedule(){
        Map<String, JobParameter> confMap = new HashMap<>();
        confMap.put("time", new JobParameter(System.currentTimeMillis()));
        JobParameters jobParameters = new JobParameters(confMap);
        try {
            jobLauncher.run(postJobConfiguration.excuteJob(), jobParameters);
        } catch (JobExecutionAlreadyRunningException | JobInstanceAlreadyCompleteException
                 | JobParametersInvalidException | org.springframework.batch.core.repository.JobRestartException e) {
            log.error(e.getMessage());
        }
    }
}

1)배치job을 실행시키기 위한 jobluncher와 앞으로 만들 배치job파일(PostJobConfiguration)을 Autowire시킨다.
2)repository를 생성자를 통해 주입한다.
3)job을 run시키려면 jobparameter값을 필수로 주어야 하는데, 파라미터 값으로 현재시간을 주게 되면 Database에 아래와 같이 저장이 된다. 이를 가지고, JOB EXCUTION의 KEY값으로 사용할 수 있고, 로깅이나 추적 등의 방법으로도 활용이 가능 할 것이다.


배치job프로그램

@Slf4j
@RequiredArgsConstructor
@Configuration // Spring Batch의 모든 Job은 @Configuration으로 등록해서 사용해야 한다.
public class PostJobConfiguration {
    private final JobBuilderFactory jobBuilderFactory;
    private final StepBuilderFactory stepBuilderFactory;
    private final PostRepository repository;
    public Job excuteJob() {
        return jobBuilderFactory.get("excuteJob")
                .start(firstStep())
                .build();
    }
    @Bean
    public Step firstStep() {
        return stepBuilderFactory.get("firstStep")
                .tasklet((contribution, chunkContext) -> {
                    log.info("Start");
                    String scheduled = "checked";
                    for (Post post : repository.findByScheduled(scheduled)) {
                        if(post.getScheduledTime().isBefore(LocalDateTime.now().plusSeconds(1))){
                            post.setScheduled(null);
                            repository.save(post);
                        }
                    }
                    return RepeatStatus.FINISHED;
                })
                .build();
    }
}

1)job, step의 개념이 나오는데, job은 실행의 주체이고 step은 step1, step2등 1가지 job내에서 여러 스탭을 실행할 수 있는 단위라고 생각하면 된다. db작업이나 파일작업 특성상 step1을 실행한 결과로 step2를 실행하는 등의 구성이 많기 때문이다.
2)위의 코드와 같이 실질적인 비지니스 로직은 step에서 수행한다 보면 될것이다.
3)firstStep에서 DB를 조회한뒤, 현재시간이 scheduledTime을 초과하게 되면 scheduled컬럼을 null로 세팅하도록 하였다.(앞서 말한 Scheduler 글을 참고하면 이해가 될 것이다.)


혹시나 실행시 BAT_JOB DB관련 에러가 난다면 아래 내용을 참고하시길 바란다.
sql문 실행

/spring-batch-core-4.3.7.jar!/org/springframework/batch/core/schema-mysql.sql

Database가 구성되면, 위의 경로의 라이브러리에 내장되어 있는 쿼리를 실행하자. batch_job과 관련있는 table을 DB에 추가해줘야지만 오류가 나지 않는다.

728x90