[Spring Batch] Job 중복 실행 방지

Taeho·2022년 7월 28일
0

SpringFramework

목록 보기
2/4

Quartz를 이용해 Job을 스케쥴링할 때 Job의 수행시간이 스케쥴러의 주기 보다 길어지면 Job이 중복 실행되면서 문제가 발생할 수 있다.

예를들어 내부 시스템으로 접수된 적하목록에 대해 검사선별을 위해 선별배치가 수행되는데, 언제 접수될 지 모르는 적하목록을 폴링해야 하므로 데몬 배치로 개발되었으나, 스프링배치를 적용 하면서 1분을 주기로 스케쥴링을 하게된다. 이때 처음 실행된 Job이 적하목록 선별 건수가 많아 1분을 경과하여 실행될 때 새로운 스케쥴러에 의해 Job이 중복 수행되면 선별처리에 문제가 생기게 된다.

Job을 구현한 클래스에 @DisallowConcurrentExecution 어노테이션을 적용하면 동시성 방지 처리가 된다.

동시성 방지 처리 전 실행결과

→ 매 1분마다 Job 실행

동시성 방지 처리 후 실행결과

→ sleep 타임 이후 Job 실행

적용 코드

context-quartz.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

    <!-- Service Class -->
    <bean id="selectivityJobService" class="py.gov.rms.sm.batch.selectivity.service.SelectivityJobService" />

    <bean name="selectivityJob" class="org.springframework.scheduling.quartz.JobDetailFactoryBean"><!-- Quartz 2.x 버전 -->
        <property name="jobClass">
            <value>py.gov.rms.sm.batch.selectivity.scheduler.SelectivityJobMain</value>
        </property>
        <!--jobClass에 넘길 데이터에 위에 선언한 Service 세팅 -->
        <property name="jobDataAsMap">
            <map>
                <entry key="selectivityJobService" value-ref="selectivityJobService"/>
            </map>
        </property>
    </bean>

    <bean id="selectivityTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean"><!-- Quartz 2.x 버전 -->
        <property name="jobDetail">
            <ref bean="selectivityJob"/>
        </property>
        <property name="cronExpression">
            <value>0 0/1 * * * ?</value>
        </property>
    </bean>

    <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <property name="triggers">
            <list>
                <ref bean="selectivityTrigger"/>
            </list>
        </property>
    </bean>
</beans>

SelectivityJobMain.java

package py.gov.rms.sm.batch.selectivity.scheduler;

import org.quartz.DisallowConcurrentExecution;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.quartz.QuartzJobBean;

import py.gov.rms.sm.batch.selectivity.service.SelectivityJobService;

// 동시성 방지 처리
@DisallowConcurrentExecution
public class SelectivityJobMain extends QuartzJobBean {

    private final Logger logger = LoggerFactory.getLogger( SelectivityJobMain.class );

    /** Service를 사용하기위해서는 "context-quartz.xml"에 <bean>으로 등록후
     *  FactoryBean의 "jobDataAsMap" 설정을 해줘야함
     */
    private SelectivityJobService selectivityJobService;

    public void setSelectivityJobService( SelectivityJobService selectivityJobService) {
        this.selectivityJobService = selectivityJobService;
    }

    @Override
    public void executeInternal( JobExecutionContext context ) throws JobExecutionException {

        logger.debug( "Selectivity Job executeInternal" );

        try {
            selectivityJobService.preManifestSelectivity();

        } catch (Exception e) {
            e.printStackTrace();
        }

    }

}

SelectivityJobService.java

package py.gov.rms.sm.batch.selectivity.service;

import java.util.HashMap;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import py.gov.rms.common.util.CommonUtil;

@Service( "selectivityJobService" )
public class SelectivityJobService {

    private final Logger logger = LoggerFactory.getLogger( SelectivityJobService.class );

    public void preManifestSelectivity() throws Exception {

        logger.debug(">>>> preManifestSelectivity...");

        try {
            Thread.sleep(1000 * 80); //1분 20초 대기
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("sleep 실행 후");


    }

    public static void main(String[] args) {
        // TODO Auto-generated method stub

    }

}

참고자료

Quartz crontrigger 중복 실행 방지 어노테이션

profile
한걸음 더 내딛는 개발자

0개의 댓글