Spring Batch : JobExplorer, JobRegistry,JobOperation,

    이 게시글은 인프런 정수원님의 강의를 복습하며 작성한 글입니다

    JobExplorer, JobRegistry, JobOperation

    Spring Batch는 Batch 처리 도움을 주기 위해 위의 객체를 제공한다. 위의 객체에 대한 정리는 아래에서 살펴보자.

    JobExplorer

    • JobExplorer는 JobRepository의 읽기전용 모드임. 
    • 실행 중인 Job의 실행 정보인 JobExecution, StepExecution을 조회할 수 있음.  → 꺼내서 Stop하는데 도움됨.

    JobRegistry

    • 생성된 Job을 자동으로 등록, 추적 및 관리하며 여러 곳에서 Job을 생성한 경우 스프링 컨테이너에서 Job을 수집해서 사용할 수 있음. 
    • Job Registry의 기본 구현체는 MapJobRegistry임. Map기반이고 Key는 JobName임.
    • Job 등록은 스프링 컨테이너가 시작할 때, JobRegistryBeanPostProcessor에 의해서 자동으로 JobRegistry에 Job을 등록시켜줌.  따라서 JobRegistryBeanPostProcessor를 스프링 빈으로 등록해야함.
    • Job Registry는 내부적으로 Job Factory를 가지고 있고, Job이 필요할 때 꺼내와서 Job을 생성함. 

    JobOperator → 주로 이걸 사용함. 

    • JobExplorer, JobRepository, JobRegistry, JobLauncher를 가지고 있음. 
    • Batch Job의 중단, 재시작, Job 요약 등의 모니터링이 가능함. 
    • 기본 구현체로 SimpleJobOperator가 제공됨. 

     

     

     

    JobExplorer, JobRegistry, JobOperation의 활용방안

    위 객체들로 개발자는 어떤 일을 할 수 있을까? JobOperation을 이용해서 Job을 쉽게 실행할 수 있고, 실행중인 Job을 멈추는 작업을 할 수 있기 때문에 활용 방안이 무궁무진하다. 만약에 나라면 토이 프로젝트에서 Session Listener를 이용해서 필요한 작업을 주기적으로 처리할 수도 있고, Controller를 하나 따서 요청이 올 때 마다 Job을 실행하는 쪽에도 활용할 것이다. 

     

     

     

    JobOperation은 JobRegistry를 가짐.

    JobRegistry는 기본적으로 스프링이 등록해주는 객체다. JobRegistryPostBeanProcessor를 등록해주면, 이 빈 후처리기는 JobRegistry에 빈으로 등록된 모든 Job을 JobFactory로 등록해준다. 그리고 JobRegistry는 getJob()을 통해서 JobRegistry가 가지고 있는 JobFactory를 꺼내와 Job을 가질 수 있다. 

    JobRegistry

    JobOperation은 내부적으로 JobRegistry를 참조한다. 따라서 JobRegistry를 통해서 Job을 가지고 와서 그 Job을 실행할 수 있다. 이 때, JobName 정도만 전달해주면 Job을 찾을 수 있고, 찾아온 Job에 Job Parameter를 더하면서 실행할 수 있게 된다. 

     

     

    JobExplorer, JobRegistry, JobOperation 코드 작성 

    이번 코드는 Job을 하나 구성한다. 그리고 JobRegistryBeanPostProcessor를 등록해서 JobRegistry에 Job을 저장할 수 있게 한다. 그리고 컨트롤러를 하나 따서, 컨트롤러에 요청이 오면 JobOperation을 통해서 Job을 실행할 수 있도록 한다. 

     

    JobRegistryBeanPostProcessor 등록 

    @Bean
    public JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor(JobRegistry jobRegistry) {
        JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor = new JobRegistryBeanPostProcessor();
        jobRegistryBeanPostProcessor.setJobRegistry(jobRegistry);
        return jobRegistryBeanPostProcessor;
    }

    JobRegistry는 스프링 빈이 시작할 때 등록해준다. 개발자는 DI 받은 JobRegistry를 JobPostBeanProcessor에 등록해준다. 이 과정이 거쳐지면 JobRegistry는 빈으로 등록되는 Job을 가지게 된다. 

     

    Job 구성

    @RequiredArgsConstructor
    @Configuration
    public class JobOperatorRetryConfig {
    
    
        private final JobBuilderFactory jobBuilderFactory;
        private final StepBuilderFactory stepBuilderFactory;
        private final JobRegistry jobRegistry;
    
    
        @Bean
        public Job jobOperationRetryJob() {
            return jobBuilderFactory.get("jobOperationRetryJob")
                    .incrementer(new RunIdIncrementer())
                    .start(step1())
                    .next(step2())
                    .build();
        }
    
        @Bean
        public Step step1() {
            return stepBuilderFactory.get("jobOperationRetryStep")
                    .tasklet(new Tasklet() {
                        @Override
                        public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
                            System.out.println("step1 executed!");
                            Thread.sleep(10000);
                            System.out.println("step1 complted!");
                            return RepeatStatus.FINISHED;
                        }
                    })
                    .build();
        }
    
        @Bean
        public Step step2() {
            return stepBuilderFactory.get("jobOperationRetryStep2")
                    .tasklet(new Tasklet() {
                        @Override
                        public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
                            System.out.println("step2 executed!");
                            Thread.sleep(10000);
                            System.out.println("step2 complted!");
                            return RepeatStatus.FINISHED;
                        }
                    })
                    .build();
        }

    다음과 같이 Batch Job을 구성했다. 기본적으로는 각 Step마다 10초씩 쉬어가도록 했는데, 이건 Stop 하는 동작도 구현하도록 한 것이다. 

    Controller 구성

    @Controller
    @RequiredArgsConstructor
    public class JobController {
    
        private final JobOperator jobOperator;
        private final JobExplorer jobExplorer;
    
        @RequestMapping("/batch/new")
        public String jobStart(@RequestParam(name = "id") String id) throws JobInstanceAlreadyExistsException, NoSuchJobException, JobParametersInvalidException {
            Set<String> jobNames = jobOperator.getJobNames();
            for (String jobName : jobNames) {
                jobOperator.start(jobName, id + "abc");
            }
            System.out.println("Job Start!");
            return "batch Job start!";
        }
    
        @RequestMapping("/batch/stop")
        public String jobStop() throws JobInstanceAlreadyExistsException, NoSuchJobException, JobParametersInvalidException, NoSuchJobExecutionException, JobExecutionNotRunningException {
    
            System.out.println("Job Stop!");
    
            Set<String> jobNames = jobOperator.getJobNames();
    
            Set<Long> jobOperationRetryJob = jobOperator.getRunningExecutions("jobOperationRetryJob");
            for (Long aLong : jobOperationRetryJob) {
                jobOperator.stop(aLong);
            }
    
            return "batch Job stop!";
        }
    
        @RequestMapping("/batch/re")
        public String jobRetry(@RequestParam(name = "id") String id) throws JobInstanceAlreadyExistsException, NoSuchJobException, JobParametersInvalidException, JobInstanceAlreadyCompleteException, NoSuchJobExecutionException, JobRestartException {
            System.out.println("Job Retry!");
            Set<String> jobNames = jobOperator.getJobNames();
    
            JobInstance lastJobInstance = jobExplorer.getLastJobInstance("jobOperationRetryJob");
            JobExecution lastJobExecution = jobExplorer.getLastJobExecution(lastJobInstance);
    
            jobOperator.restart(lastJobExecution.getId());
            return "batch Job Retry!";
        }
    
    
    
    
    }
    • Controller를 통해 Job의 실행, 실행중인 Job의 중단, 중단된 Job의 재시작을 할 수 있도록 했다. 
    • JobOperation을 통해서 start / stop / restart를 통해서 구현할 수 있다.
    • 이 때, JobOperation에 jobExecution이나 jobExecutionId를 던져줘야하는데 이것들은 JobExplorer, JobOperator에서 충분히 찾을 수 있다. 
    • 기본적으로 JobOperation에 jobNames()를 하면 Collection 자료 구조로 반환이 되는데, 이것은 JobRegistry 내부에 여러 Job이 등록될 수 있기 때문이다. 
    • retry 메서드는 lastJobInstance()를 가져와야한다. 왜냐하면 실패한 잡은 가장 마지막에 있기 때문이다.

     

    JobOperation을 통한 JobStop 시 유의사항.

    jobOperation을 통해 Job을 중지 시킬 수 있다. 이 때 유의할 사항은 Stop이 실행되더라도, 현재 실행 중인 Step까지는 모두 완료하고 Job을 중단한다는 것이다. 예를 들어 Step1, Step2를 해야하는데 Step1을 실행하는 중 Stop 명령을 받으면 Step1을 완료한 상태에서 Job을 종료한다. 

     

    테스트 코드 확인

     

     

    GitHub - chickenchickenlove/springbatchstudy

    Contribute to chickenchickenlove/springbatchstudy development by creating an account on GitHub.

    github.com

     

    댓글

    Designed by JB FACTORY