ShedLock을 이용한 Master - Slave (primary - secondary, leader - follower) 구분 없는 스케줄러 만들기

스케줄을 처리하는 인스턴스 A와 B가 떠 있을때, 별도의 Master - Slave 구분 없이 둘 중 하나의 인스턴스가 처리하도록 하려면 어떻게 해야 할까요?

troubleshooting

어쩔때는 A 가 돌고, 어쩔때는 B 가 돌고...

A 가 죽어도 알아서 B 가 돌고 하는 구성을 만들고 싶을때 말이죠.

이런 고민을 하다가 얼마전 발견한 라이브러리 입니다.

https://github.com/lukas-krecan/ShedLock

일반적인 경우 Quartz 를 사용하거나 Spring 에서 제공하는 스케줄러를 사용해 주기적인 작업을 처리하도록 개발합니다.

그리고, 많은 경우 배치 인스턴스를 만들면 하나만 띄워서 실행을 해요. 그러다가 죽기라도 하면,

  • 업무시간에 죽으면 그냥 재시작 하면 되지만
  • 집에서 자다가 알림 받고 인스턴스를 다시 띄우거나,
  • 애기보다가 알림을 못 받을 수도 있고,
  • 술먹고 기절이라도 했다면 몇 시간동안 스케줄된 작업이 돌지 않겠죠.

그래서 나온 것이 바로 ShedLock 입니다.

ShedLock 은

  1. 저장할 대상을 선정한 후
  2. 데이터 저장소에 테이블 하나 생성해주고
  3. Maven Dependency 를 추가해주고
  4. Bean 하나 Copy & Paste 하고

    5.스케줄 메소드에 어노테이션 추가만 해주면

    끝납니다.

이렇게 5단계로 진행한다고 해서 복잡해 보일 수도 있는데, 실제로 적용해 보면 아주 간단해요.

사용 가능한 저장소는 다음과 같습니다.

  • Mongo
  • DynamoDB
  • JdbcTemplate
  • ZooKeeper (using Curator)
  • Redis (using Spring RedisConnectionFactory)
  • Redis (using Jedis)
  • Hazelcast

그러면 회사에서 주력으로 사용하는 mySql 을 예로 들어 볼게요.

  1. 저장할 대상 : mySql
  2. 테이블 생성

    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)
    )

     

  1. 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>
    

     

  1. Bean 추가

    1
    2
    3
    4
    5
    6
    import net.javacrumbs.shedlock.provider.jdbctemplate.JdbcTemplateLockProvider;
    ...
    @Bean
    public LockProvider lockProvider(DataSource dataSource) {
        return new JdbcTemplateLockProvider(dataSource);
    }

  1. 어노테이션 추가 @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번에서 생성한 테이블에 값 하나를 저장해 놓고 작업을 실행하고, 다른 인스턴스는 테이블에 값이 업데이트 되어 있기 때문에 일을 안하고 잠을 잡니다.

테스트를 해 본 결과 하나만 지속적으로 사용하지는 않고 두개의 인스턴스가 규칙 없이(?) 일을 나누어 실행하는 것을 확인할 수 있었습니다.


Popit은 페이스북 댓글만 사용하고 있습니다. 페이스북 로그인 후 글을 보시면 댓글이 나타납니다.