skill/Java.Kotlin

springboot @Scheduler 다중 서버에서 한번만 실행 : shedlock

have a nice day :D 2023. 11. 21. 16:33
반응형

springboot @Scheduler 이용 시, 서버가 여러개 돌아가면 각 서버마다 @Scheduler가 돌아가 중복 처리 됨.
Shedlock을 사용하면 스케쥴러가 한 번만 실행 됨.

pom.xml 의존성 추가

<dependency>
    <groupId>net.javacrumbs.shedlock</groupId>
    <artifactId>shedlock-spring</artifactId>
    <version>2.2.0</version>
</dependency>
<dependency>
    <groupId>net.javacrumbs.shedlock</groupId>
    <artifactId>shedlock-provider-jdbc-template</artifactId>
    <version>2.1.0</version>
</dependency>


config 추가 

import net.javacrumbs.shedlock.core.LockProvider;
import net.javacrumbs.shedlock.provider.jdbctemplate.JdbcTemplateLockProvider;
import net.javacrumbs.shedlock.spring.annotation.EnableSchedulerLock;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.scheduling.annotation.EnableScheduling;

import javax.sql.DataSource;

@Configuration
@EnableScheduling
@EnableSchedulerLock(defaultLockAtMostFor = "PT30S") // 기본 30초
public class SchedulerLockConfig {
    @Bean
    public LockProvider lockProvider(DataSource dataSource) {
        return new JdbcTemplateLockProvider(
                JdbcTemplateLockProvider.Configuration.builder()
                        .withJdbcTemplate(new JdbcTemplate(dataSource))
                        .withTableName("shedlock") // table 명 변경 가능
                        .build()
        );
    }
}


table 추가

CREATE TABLE shedlock (
  name VARCHAR(64),
  lock_until TIMESTAMP(3) NULL,
  locked_at TIMESTAMP(3) NULL,
  locked_by VARCHAR(255),
  PRIMARY KEY (name)
);


batch 실행

import javax.annotation.PostConstruct;
import net.javacrumbs.shedlock.core.SchedulerLock;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Component
@RequiredArgsConstructor
@Slf4j
public class SchedulerExecutor {
	private final TestTask testTask;

	@Scheduled(cron = "0 * * * * *")
	@SchedulerLock(name = "testTask",	lockAtLeastForString = "PT10S", lockAtMostForString = "PT10S")
	public void testTask() throws InterruptedException {
		testTask.execute();
	}
}



전처리, 후처리 필요 시 : pom.xml 추가

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>


전처리, 후처리 필요 시 : config  추가

import com.clubmeta_v2.batch.service.BatchV2Service;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;

@Aspect
@Component
@EnableAspectJAutoProxy
@Slf4j
public class SchedulerAspect {
    //@Autowired
    //private SchedulerV2Service schedulerV2Service;

    @Around("@annotation(org.springframework.scheduling.annotation.Scheduled)")
    public Object aroundSchedule(ProceedingJoinPoint joinPoint) throws Throwable {
        String methodName = joinPoint.getSignature().getName();
        try {
            // 스케쥴러 전처리 작성
            
            return joinPoint.proceed(); // 스케쥴러 실행
        } catch (Throwable e) {
            throw e;
        } finally {
        	// 스케쥴러 후처리 작성
        }
    }



[참고] https://www.baeldung.com/shedlock-spring

반응형