Springboot Quartz clustering 적용방법, spring batch, shedlock
quartz를 사용하여 schedulering을 하는 어플리케이션을 이중화 하게 되면, 복수 개의 scheduler가 동작하기 때문에 문제를 야기 할 수 있습니다.
그래서 Clustering을 적용하여 하나의 scheduler만 동작하게 처리해야 합니다.
그 방법중에 하나가 Quartz clustering이고 shedlock 등의 라이브러리도 DB를 사용한 Clustering을 제공합니다.
Quartz clustering 적용방법
그럼 이제 quartz에 clustering을 적용하는 방법을 알아보도록 하겠습니다.
org.quartz.simpl.RAMJobStore 는 default 로 메모리를 사용한 스케쥴링이고,
org.quartz.impl.jdbcjobstore.JobStoreTX 가 DB를 활용한 스케쥴링 입니다.
1.quartz.properties 생성
quartz clustering을 위한 설정파일을 생성합니다.
설정 내용은 아래의 Link를 확인하세요.
위의 Link를 참고하여 작성한 properties 파일의 내용입니다.
한글로 처리된 부분만 자신의 어플리케이션에 맞게 수정해주세요.
#============================================================================
# Configure Main Scheduler Properties
#============================================================================
org.quartz.scheduler.instanceName = [스케쥴러 이름]
org.quartz.scheduler.instanceId = AUTO
#============================================================================
# Configure ThreadPool
#============================================================================
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 10
org.quartz.threadPool.threadPriority = 5
#============================================================================
# Configure JobStore
#============================================================================
org.quartz.jobStore.misfireThreshold = 60000
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
org.quartz.jobStore.useProperties = false
org.quartz.jobStore.dataSource = quartzDataSource
org.quartz.jobStore.tablePrefix = [스키마].qrtz_
org.quartz.jobStore.isClustered = true
org.quartz.jobStore.clusterCheckinInterval = 20000
#============================================================================
# Configure Datasources
#============================================================================
org.quartz.dataSource.quartzDataSource.provider=hikaricp
org.quartz.dataSource.quartzDataSource.driver = org.postgresql.Driver
org.quartz.dataSource.quartzDataSource.URL = jdbc:postgresql://[데이터베이스 경로]
org.quartz.dataSource.quartzDataSource.user = [유저]
org.quartz.dataSource.quartzDataSource.password = [패스워드]
org.quartz.dataSource.quartzDataSource.maxConnections = 5
org.quartz.dataSource.quartzDataSource.validationQuery=select 1
이제 쿼즈에서 사용하는 테이블과 인덱스 등의 정보를 생성해 주어야 합니다.
아래의 quartz github로 이동하여 소스코드를 다운로드 받아도 되고, github에서 보여도 됩니다.
경로 : quartz-2.3.2\quartz-2.3.2\quartz-core\src\main\resources\org\quartz\impl\jdbcjobstore
Download Link : https://github.com/quartz-scheduler/quartz/releases/
Github Link : https://github.com/quartz-scheduler/quartz/tree/main/quartz-core/src/main/resources/org/quartz/impl/jdbcjobstore
이동하면 DB 종류에 따라 sql 파일이 존재하며, 자신의 DB 에 맞는 파일의 내용을 복사한 뒤 DB스크립트를 실행해 주시면 됩니다.
저의 경우 postgresql을 사용하기 때문에 tables_postgres.sql 파일을 내용을 스크립트에서 실행했습니다.
그럼 아래와 같은 테이블이 생성되고, 필요한 인덱스 및 기타 설정들이 추가되게 됩니다.
참고용으로 Config Class 를 보여드리겠습니다.
@Configuration
public class ScheduleConfig {
private static final Logger LOGGER = LoggerFactory.getLogger(ScheduleConfig.class);
private SchedulerFactory schedulerFactory = new StdSchedulerFactory();
@PostConstruct
public void start() throws SchedulerException {
LOGGER.info("[N]Start to Scheduler for send message and user unregister.");
schedulerFactory = new StdSchedulerFactory();
Scheduler scheduler = schedulerFactory.getScheduler();
scheduler.start();
TriggerKey sendMsgTriggerKey = new TriggerKey("sendMsg");
scheduler.unscheduleJob(sendMsgTriggerKey);
JobDetail sendMsg = JobBuilder.newJob(MsgJobClient.class).build();
Trigger sendTrigger = TriggerBuilder.newTrigger().withIdentity(sendMsgTriggerKey)
.withSchedule(CronScheduleBuilder.cronSchedule("30 0/10 * * * ?")).build();
scheduler.scheduleJob(sendMsg, sendTrigger);
TriggerKey unregisterTriggerKey = new TriggerKey("unregister");
scheduler.unscheduleJob(unregisterTriggerKey);
JobDetail unresisterUser = JobBuilder.newJob(UnregisterClient.class).build();
Trigger unresisterTrigger = TriggerBuilder.newTrigger().withIdentity(unregisterTriggerKey)
.withSchedule(CronScheduleBuilder.cronSchedule("0 0 0 * * ?")).build();
scheduler.scheduleJob(unresisterUser, unresisterTrigger);
}
}
두개의 job이 동작하는 설정파일 입니다. 기존에는 unscheduleJob이 없이도 잘 동작하였으나, clustering 적용 시 문제가 되어 unscheduleJob을 추가하였습니다.
이제 프로젝트를 실행하면 아래와 같은 메시지가 표출되고, DB의 qrtz_scheduler_state 테이블을 보시면 데이터가 추가된 것을 확인하실 수 있습니다.
이제 프로젝트의 포트를 변경하여 .jar 파일을 만들어 2개의 프로젝트를 실행하면 하나의 프로젝트에서만 scheduler가 동작하는 것을 확인하실 수 있습니다.
yml으로 properties를 설정하려고 했는데 Document에서 찾지못해 properties 로 추가하였습니다.
여타 다른 블로그에 나온 설정으로는 동작하지 않더군요, 아시는 분은 댓글 부탁드립니다.