[공부정리] Quartz 사용해보기

jeyong·2024년 3월 25일
0

공부 / 생각 정리  

목록 보기
62/120
post-custom-banner


이번 게시글에서는 Quartz 스케줄러를 활용한 경험을 공유하려고 한다. Quartz를 도입하면서 필요했던 기본 설정 과정이 예상외로 복잡했기 때문에, 이에 대해서 기록해두려고 한다.

1. 기본 설정

Configuration of Quartz is typically done through the use of a properties file, in conjunction with the use of StdSchedulerFactory (which consumes the configuration file and instantiates a scheduler).

By default, StdSchedulerFactory load a properties file named “quartz.properties” from the ‘current working directory’. If that fails, then the “quartz.properties” file located (as a resource) in the org/quartz package is loaded. If you wish to use a file other than these defaults, you must define the system property ‘org.quartz.properties’ to point to the file you want.

Quartz Configuration Reference

1-1. JDBC 기반 저장소 설정 및 기본 설정

application.yml

spring:
  quartz:
    scheduler-name: QuartzScheduler # Quartz 스케줄러 이름
    jdbc:
      initialize-schema: never # Quartz만의 저장소 사용을 위한 스키마 수동 생성
    job-store-type: jdbc # JobStore 유형 설정
    
logging:
  level:
    org:
      quartz: DEBUG # Quartz 로그 레벨 설정

Quartz Scheduler

1-2. Configure Clustering with JDBC-JobStore

Load-balancing occurs automatically, with each node of the cluster firing jobs as quickly as it can. When a trigger’s firing time occurs, the first node to acquire it (by placing a lock on it) is the node that will fire it.

Only one node will fire the job for each firing. What I mean by that is, if the job has a repeating trigger that tells it to fire every 10 seconds, then at 12:00:00 exactly one node will run the job, and at 12:00:10 exactly one node will run the job, etc. It won’t necessarily be the same node each time - it will more or less be random which node runs it. The load balancing mechanism is near-random for busy schedulers (lots of triggers) but favors the same node for non-busy (e.g. few triggers) schedulers.

Configure Clustering with JDBC-JobStore

quartz.properties

# 스케줄러 기본 설정
org.quartz.scheduler.instanceName=QuartzScheduler
org.quartz.scheduler.instanceId=AUTO

# 스레드 풀 설정
org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount=20
org.quartz.threadPool.threadPriority=5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread=true
org.quartz.threadPool.threadNamePrefix=QuartzThread-

# JobStore 설정
org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.tablePrefix=QRTZ_
org.quartz.jobStore.isClustered=true
org.quartz.jobStore.misfireThreshold=1000
org.quartz.jobStore.clusterCheckinInterval=20000
# org.quartz.jobStore.useProperties=true
org.quartz.jobStore.dataSource=quartz

# 데이터 소스 설정
org.quartz.dataSource.quartz.provider=hikaricp
org.quartz.dataSource.quartz.driver=com.mysql.cj.jdbc.Driver
org.quartz.dataSource.quartz.URL=jdbc:mysql://127.0.0.1:3306/quartz?useSSL=false&useUnicode=true&serverTimezone=Asia/Seoul&allowPublicKeyRetrieval=true
org.quartz.dataSource.quartz.user=root
org.quartz.dataSource.quartz.password=2721
org.quartz.dataSource.quartz.maxConnections=30

Configure Main Scheduler Settings
Configure DataSources

1-3. Qyartz.sql

앞에서 initialize-schema: never을 통해 스키마를 수동으로 생성해주어야한다.

uartz-scheduler/distribution/src/main/assembly/root/docs/dbTables/

1-4. quartz.properties 적용 및 기타 설정

AutoWiringSpringBeanJobFactory

public class AutoWiringSpringBeanJobFactory extends SpringBeanJobFactory implements ApplicationContextAware {
    private transient AutowireCapableBeanFactory beanFactory;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext)
            throws BeansException {
        beanFactory = applicationContext.getAutowireCapableBeanFactory();
    }

    @Override
    protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
        final Object job = super.createJobInstance(bundle);
        beanFactory.autowireBean(job);

        return job;
    }
}

@Autowired를 사용하기 위해 설정한다.

QuartzConfig

@Slf4j
@Configuration
@RequiredArgsConstructor
public class QuartzConfig {
    private final ApplicationContext applicationContext;
    private final PlatformTransactionManager platformTransactionManager;

    @Bean
    public SchedulerFactoryBean schedulerFactoryBean() {
        SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
        AutoWiringSpringBeanJobFactory autoWiringSpringBeanJobFactory = new AutoWiringSpringBeanJobFactory();

        autoWiringSpringBeanJobFactory.setApplicationContext(applicationContext);
        schedulerFactoryBean.setJobFactory(autoWiringSpringBeanJobFactory);
        schedulerFactoryBean.setOverwriteExistingJobs(true);
        schedulerFactoryBean.setAutoStartup(true);
        schedulerFactoryBean.setTransactionManager(platformTransactionManager);
        schedulerFactoryBean.setQuartzProperties(quartzProperties());
        return schedulerFactoryBean;
    }

    private Properties quartzProperties() {
        PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
        propertiesFactoryBean.setLocation(new ClassPathResource("quartz.properties"));
        Properties properties = null;

        try {
            propertiesFactoryBean.afterPropertiesSet();
            properties = propertiesFactoryBean.getObject();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return properties;
    }

}

참고로 해당 설정을 하여도 @RequiredArgConstructor가 작동하지 않으므로 @Autowired를 사용해야한다.

설정하지 않으면 사진처럼 의존성을 주입받지 못한다.

3. Clustering

크론 표현식을 이용하여 매분 5초일때 마다 job을 실행하도록 하였다.

크론 표현식 (Cron Expression) 정리

매분 5초일때 마다 다른 인스턴스에서 작동하는 모습이다.

It won’t necessarily be the same node each time - it will more or less be random which node runs it. The load balancing mechanism is near-random for busy schedulers (lots of triggers) but favors the same node for non-busy (e.g. few triggers) schedulers.

공식문서에서 설명한 내용처럼 랜덤한 방식으로 인스턴스를 선정하는 모습도 볼 수 있다.

profile
노를 젓다 보면 언젠가는 물이 들어오겠지.
post-custom-banner

0개의 댓글