ShedLock을 이용한 Master - Slave (primary - secondary, leader - follower) 구분 없는 스케줄러 만들기
스케줄을 처리하는 인스턴스 A와 B가 떠 있을때, 별도의 Master - Slave 구분 없이 둘 중 하나의 인스턴스가 처리하도록 하려면 어떻게 해야 할까요?
어쩔때는 A 가 돌고, 어쩔때는 B 가 돌고...
A 가 죽어도 알아서 B 가 돌고 하는 구성을 만들고 싶을때 말이죠.
이런 고민을 하다가 얼마전 발견한 라이브러리 입니다.
https://github.com/lukas-krecan/ShedLock
일반적인 경우 Quartz 를 사용하거나 Spring 에서 제공하는 스케줄러를 사용해 주기적인 작업을 처리하도록 개발합니다.
그리고, 많은 경우 배치 인스턴스를 만들면 하나만 띄워서 실행을 해요. 그러다가 죽기라도 하면,
- 업무시간에 죽으면 그냥 재시작 하면 되지만
- 집에서 자다가 알림 받고 인스턴스를 다시 띄우거나,
- 애기보다가 알림을 못 받을 수도 있고,
- 술먹고 기절이라도 했다면 몇 시간동안 스케줄된 작업이 돌지 않겠죠.
그래서 나온 것이 바로 ShedLock 입니다.
ShedLock 은
- 저장할 대상을 선정한 후
- 데이터 저장소에 테이블 하나 생성해주고
- Maven Dependency 를 추가해주고
- Bean 하나 Copy & Paste 하고
5.스케줄 메소드에 어노테이션 추가만 해주면
끝납니다.
이렇게 5단계로 진행한다고 해서 복잡해 보일 수도 있는데, 실제로 적용해 보면 아주 간단해요.
사용 가능한 저장소는 다음과 같습니다.
- Mongo
- DynamoDB
- JdbcTemplate
- ZooKeeper (using Curator)
- Redis (using Spring RedisConnectionFactory)
- Redis (using Jedis)
- Hazelcast
그러면 회사에서 주력으로 사용하는 mySql 을 예로 들어 볼게요.
- 저장할 대상 : mySql
- 테이블 생성
1 2 3 4 5 6 7
CREATE TABLE shedlock( name VARCHAR(64), lock_until TIMESTAMP(3) NULL, locked_at TIMESTAMP(3) NULL, locked_by VARCHAR(255), PRIMARY KEY (name) )
- Maven Dependency
1 2 3 4 5
<dependency> <groupId>net.javacrumbs.shedlock</groupId> <artifactId>shedlock-provider-jdbc-template</artifactId> <version>2.4.0</version> </dependency>
- Bean 추가
1 2 3 4 5 6
import net.javacrumbs.shedlock.provider.jdbctemplate.JdbcTemplateLockProvider; ... @Bean public LockProvider lockProvider(DataSource dataSource) { return new JdbcTemplateLockProvider(dataSource); }
- 어노테이션 추가 @SchedulerLock 부분 참고
1 2 3 4 5 6 7
import net.javacrumbs.shedlock.core.SchedulerLock; ... @Scheduled(...) @SchedulerLock(name = "scheduledTaskName") public void scheduledTask() { // do something }
만약 스케줄러가 여러 개라면 scheduledTaskName 값을 다르게 해 주면 됩니다.
이렇게 설정하고 나서 두개의 인스턴스를 띄워 놓으면,
각각의 인스턴스 중 하나가 2번에서 생성한 테이블에 값 하나를 저장해 놓고 작업을 실행하고, 다른 인스턴스는 테이블에 값이 업데이트 되어 있기 때문에 일을 안하고 잠을 잡니다.
테스트를 해 본 결과 하나만 지속적으로 사용하지는 않고 두개의 인스턴스가 규칙 없이(?) 일을 나누어 실행하는 것을 확인할 수 있었습니다.