www.baeldung.com/shedlock-spring
같은 잡을 수행하는 각각 다른 서버에서의 인스턴스 A,B가 있을 때,
A,B 둘 중 하나가 수행하도록, 2개 이상의 서버에서 중복 수행을 방지하도록 Lock 을 걸게 하는 라이브러리로 shedlock 이 있다.
순서 관계없이 lock을 설정한 시간동안은 하나의 인스턴스만 작동하도록 되어있다.
1. application.yml에 dataSource 작성
spring:
datasource:
url: jdbc:mariadb://localhost:3306/test
driver-class-name: org.mariadb.jdbc.Driver
2. db에 테이블 생성
CREATE TABLE shedlock (
name VARCHAR(64),
lock_until TIMESTAMP(3) NULL,
locked_at TIMESTAMP(3) NULL,
locked_by VARCHAR(255),
PRIMARY KEY (name)
)
3. shedlock Dependency 주입
implementation 'net.javacrumbs.shedlock:shedlock-spring:4.14.0'
implementation 'net.javacrumbs.shedlock:shedlock-provider-jdbc-template:4.14.0'
4. bean 주입
@Configuration
public class SchedulerConfiguration {
@Bean
public LockProvider lockProvider(DataSource dataSource) {
return new JdbcTemplateLockProvider(dataSource);
}
}
5. 스케줄 메소드에 에노테이션 추가
스케줄 수행 클래스와 스케줄 메소드에 각각 @EnablsScheduleLock, @SchedulerLock 어노테이션을 붙여준다.
package com.sdk2.demo.job;
import lombok.extern.slf4j.Slf4j;
import net.javacrumbs.shedlock.core.SchedulerLock;
import net.javacrumbs.shedlock.spring.annotation.EnableSchedulerLock;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.util.Date;
@Slf4j
@Component
@EnableSchedulerLock(defaultLockAtMostFor = "PT10S")
public class SchedulerJob {
@Scheduled(fixedRate = 5000)
@SchedulerLock(name="SchedulerLock",lockAtMostForString = "PT10S", lockAtLeastForString = "PT10S")
public void SchedulerTest() {
log.info("1번 스케줄러"+new Date());
}
}
package com.member.member.job;
import lombok.extern.slf4j.Slf4j;
import net.javacrumbs.shedlock.core.SchedulerLock;
import net.javacrumbs.shedlock.spring.annotation.EnableSchedulerLock;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.util.Date;
@Slf4j
@Component
@EnableSchedulerLock(defaultLockAtMostFor = "PT10S")
public class SchedulerJob {
@Scheduled (fixedRate = 5000)
@SchedulerLock (name="SchedulerLock",lockAtMostForString = "PT10S", lockAtLeastForString = "PT10S")
public void SchedulerTest() {
log.info("2번 스케줄러"+new Date());
}
}
SchedulerLock 의 속성 세 가지
@SchedulerLock 의 name은 스케줄러 락을 구분하는 고유한 구분자이며, 두 개의 메소드에 같은 이름을 주면 각각 설정한 시간에 맞춰 순서에 관계없이 중복되지 않게 스케줄러 잡을 수행한다.
We've also added a couple of optional parameters.
First, we've added lockAtLeastForString so that we can put some distance between method invocations. Using “PT5M” means that this method will hold the lock for 5 minutes, at a minimum. In other words, that means that this method can be run by ShedLock no more often than every five minutes.
Next, we added lockAtMostForString to specify how long the lock should be kept in case the executing node dies. Using “PT14M” means that it will be locked for no longer than 14 minutes.
In normal situations, ShedLock releases the lock directly after the task finishes. Now, we didn't have to do that because there is a default provided in @EnableSchedulerLock, but we've chosen to override that here.
lockAtLeastForString - 메소드간의 시간차이 정함. PT5M 은 최소 5분간 이 락을 홀드하겠다는 의미임. 잠금을 유지해야 하는 기간을 지정하는 속성
lockAtMostForString - 실행 노드가 죽었을 때 이 락이 얼마나 길게 유지되어야 하는지를 특정함, 잠금을 유지해야 하는 최소 시간을 지정하는 속성
------------------------------------------------------------------------------------------------------------------------
처음 스케줄러가 시작되면 lockAtMostFor로 시간이 잡힘
이상태에서 lockAtLeastFor값이 없다면
lock_until은 해당 스케줄러의 종료시간이 됨
lockAtLeastFor이 있다면 lock_until은 해당 스케줄러의 시작시간 + lockAtLeastFor가 됨
즉 lockAtMostFor는 모종의 이유로 스케줄러에서 에러가 나거나 시간이 오래걸릴때 다른 스케줄러가 기다려주는 시간이고
lockAtLeastFor는 해당 스케줄러가 시작되고 난후 최소한 기다려주는 시간이다.
만약 lockAtMostFor값이 lockAtLeastFor보다 작다면
에러가 발생한다.
------------------------------------------------------------------------------------------------------------------------
해당 노드가 멈췄다 할지언정 락을 획득한 경우엔 적어도 {least}초 동안은 락을 획득하지 못할 것이고, 최대로 길어야 {most}초동안 락을 획득하지 못할 것이다.
출처: https://sabjili.tistory.com/entry/스프링-스케줄-락 [잡동사니 블로그]
스케줄된 메소드에 개별적으로 직접 @SchedulerLock 을 붙여 least, most 타임을 정할 수도 있고
@Scheduled(fixedRate = 5000)
@SchedulerLock(name="SchedulerLock")
public void SchedulerTest() {
log.info("1번 스케줄러"+new Date());
schedulerMapper.saveSchedulerLog();
}
메소드에 직접 붙이는 대신 Application 클래스에서 default로 설정해 줄 수도 있다.
@EnableSchedulerLock(defaultLockAtLeastFor = "PT5S",defaultLockAtMostFor = "PT10S")