jowoohyeong / STUDY

공부
0 stars 0 forks source link

[Spring Framework] 멀티 서버에서 Spring 스케줄러 중복실행 방지 #8

Open jowoohyeong opened 1 year ago

jowoohyeong commented 1 year ago
jowoohyeong commented 1 year ago

Scheduler Lock

위와 같이 2개 이상의 서버로 구성된 환경에서 중복된 Schedule이 실행되지 않도록 Lock을 걸 수 있게 만들어놓은 라이브러리이다. maven repository에 shedlock이라는 이름으로 올라와있다. 그중에 shedlock-spring과 shedlock-provider-jdbc-template을 추가한다.

shedlock-provider-jdbc-template을 추가하는 것을 보면 데이터베이스를 이용하는 것을 예상할 수 있다. RDB 말고도 maven repository 검색결과 목록에 MongoDB, Redis provider도 보이는 것을 보면 다른 저장소를 통해서도 가능한 걸로 보인다. img1 daumcdn maven repository shedlock 검색결과 페이지

jowoohyeong commented 1 year ago

pom.xml

<!-- Scheduler lock -->
<dependency>
    <groupId>net.javacrumbs.shedlock</groupId>
    <artifactId>shedlock-spring</artifactId>
    <version>3.0.0</version>
</dependency>

<!-- JDBC Template -->
<dependency>
    <groupId>net.javacrumbs.shedlock</groupId>
    <artifactId>shedlock-provider-jdbc-template</artifactId>
    <version>3.0.0</version>
</dependency>

Bean 등록

public class ScheduleController {
    @Bean
    public LockProvider lockProvider(DataSource dataSource) {
        return new JdbcTemplateLockProvider(dataSource);
    }
}

Java로 Bean 등록

<bean id="lockProvider" class="net.javacrumbs.shedlock.provider.jdbctemplate.JdbcTemplateLockProvider">
    <constructor-arg ref="dataSource"/>
</bean>

Bean 등록 방법: java or xml 선택 만약 기존에 연동된 jdbc 또는 DB Connector가 없는 경우 DB연동부터 설정해줘야 한다. 여기서 ref="dataSource"는 기존에 연결되어있는 dataSource 명을 넣어주면 된다.

DB테이블 생성

Schedule Lock을 사용하기 위해 위에서 설정한 dataSource에 해당하는 DB에 테이블 생성, 이를 통해 lock기능 사용 가능

DROP TABLE IF EXISTS `shedlock` RESTRICT;

CREATE TABLE `shedlock` (
    `name`       VARCHAR(64)  NOT NULL COMMENT '스케줄잠금이름', -- 스케줄잠금이름
    `lock_until` TIMESTAMP(3) NULL     COMMENT '잠금기간', -- 잠금기간
    `locked_at`  TIMESTAMP(3) NULL     COMMENT '잠금일시', -- 잠금일시
    `locked_by`  VARCHAR(255) NULL     COMMENT '잠금신청자' -- 잠금신청자
)
COMMENT '스케줄잠금';

ALTER TABLE `shedlock` ADD CONSTRAINT `PK_shedlock` -- 스케줄잠금 기본키
PRIMARY KEY (
    `name` -- 스케줄잠금이름
);

여기서 name은 어노테이션에서 전달할 이름이다. 고유값으로 들어가고 이름으로 스케줄을 구분한다.

jowoohyeong commented 1 year ago

@SchedulerLock

어노테이션을 사용해서 lock을 걸어준다.

@EnableScheduling
@EnableSchedulerLock(defaultLockAtMostFor = "PT30S") // Scheduler Lock 사용 가능 설정 (기본 30초동안 Lock)
public class BatchController {

    private static final String ONE_MIN = "PT1M"; // 1분동안 LOCK

    @Scheduled(cron = "0 10 * * * ?") // 초(0~59) 분(0~59) 시(0~23) 일(1~31) 월(1~12) 요일(1~7, 일요일 : 1) 연도(생략가능)
    // Schdule Lock (1분동안)
    @SchedulerLock(name = "runScenarioOneTime", lockAtMostForString = ONE_MIN, lockAtLeastForString = ONE_MIN)
    public void runScenarioOneTime() throws Exception {
        // do something...
    }

    @Scheduled(cron = "0 20 * * * ?")
    // Schdule Lock (1분동안)
    @SchedulerLock(name = "runScenarioCycle", lockAtMostForString = ONE_MIN, lockAtLeastForString = ONE_MIN)
    public void runScenarioCycle() throws Exception {
        // do something...
    }

}

이렇게 간단한 어노테이션 설정을 통해서 각 WAS가 Scheduling할 때 생성한 테이블을 참조하여 아래와 같은 작업이 이루어진다.