Initial commit

This commit is contained in:
2026-05-29 17:49:25 +09:00
commit 330105cb27
1081 changed files with 148694 additions and 0 deletions
+221
View File
@@ -0,0 +1,221 @@
= Quartz Clustering with JDBC-JobStore
== JDBC-JobStore DB 초기화
quartz db schema (배포된 sdl-appendix 내 참조) 를 사용하는 DBMS에 맞는 SQL을 실행해 JDBC-JobStore를 사용하기 위한 Table을 생성한다.
CAUTION: Tibero DB는 미지원
== application.properties
properties 파일 내용 중 Quartz JDBC-JobStore 관련 설정이다.
[source, properties]
----
# Spring Quartz 설정
spring.quartz.job-store-type=jdbc
spring.quartz.jdbc.initialize-schema=always
spring.quartz.datasource.driver-class-name=org.postgresql.Driver
spring.quartz.datasource.jdbcUrl=jdbc:postgresql://10.40.87.189:5445/SDL
spring.quartz.datasource.username=sdl5
spring.quartz.datasource.password=dlatl#123
spring.quartz.properties.org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.PostgreSQLDelegate <1>
spring.quartz.properties.org.quartz.jobStore.isClustered=true
spring.quartz.properties.org.quartz.jobStore.misfireThreshold=60000
spring.quartz.properties.org.quartz.jobStore.clusterCheckinInterval=15000
spring.quartz.properties.org.quartz.scheduler.instanceName=sdl-prod <2>
spring.quartz.properties.org.quartz.scheduler.instanceId=AUTO <3>
spring.quartz.properties.org.quartz.scheduler.rmi.export=false
spring.quartz.properties.org.quartz.scheduler.rmi.proxy=false
spring.quartz.properties.org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
spring.quartz.properties.org.quartz.threadPool.threadCount=10 <4>
spring.quartz.properties.org.quartz.threadPool.threadPriority=5
spring.quartz.properties.org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread=true
spring.quartz.properties.org.quartz.scheduler-name=QuartzScheduler
----
<1> DB 에 따라 Delegate 를 변경한다.
+
org.quartz.impl.jdbcjobstore.oracle.OracleDelegate, +
org.quartz.impl.jdbcjobstore.MSSQLDelegate, +
org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
<2> Quartz Scheduler 를 구별하는 Property 값으로 Clustering 할 서버에서는 같은 값을 세팅한다.
<3> Unique 한 값이어야 하며 AUTO 일 경우 자동 생성된다.
<4> Job 을 실행하기 위한 Thread 수
기타 자세한 Property 값은 http://www.quartz-scheduler.org/documentation/quartz-2.3.0/configuration/ConfigJDBCJobStoreClustering.html[Quartz 메뉴얼, window=_blank]을 참고 한다.
== QuartzClusteringConfig
QuartzClusteringConfig 파일은 Spring 에 등록된 CronTriggerFactoryBean 을 실행 하도록 SchedulerFactoryBean이 정의되어 있다.
[source, java]
----
@Bean
public SchedulerFactoryBean setSchedulerFactoryBean(ApplicationContext applicationContext) {
SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
AutoWiringSpringBeanJobFactory jobFactory = new AutoWiringSpringBeanJobFactory();
jobFactory.setApplicationContext(applicationContext);
schedulerFactoryBean.setJobFactory(jobFactory); <1>
schedulerFactoryBean.setDataSource(quartzDataSource()); <2>
schedulerFactoryBean.setApplicationContext(applicationContext);
schedulerFactoryBean.setTransactionManager(quartzTransactionManager()); <3>
schedulerFactoryBean.setWaitForJobsToCompleteOnShutdown(true);
schedulerFactoryBean.setStartupDelay(60);
schedulerFactoryBean.setOverwriteExistingJobs(true);
Properties properties = new Properties();
properties.putAll(quartzProperties.getProperties()); <4>
schedulerFactoryBean.setQuartzProperties(properties);
schedulerFactoryBean.setTriggers(
knoxSyncBatchConfig.knoxApprovalSyncTrigger().getObject(),
userBatchConfig.batchUserLongTermCheckTrigger().getObject(),
userBatchConfig.batchUserAuthExpiredTrigger().getObject(),
sysUseLogBatchConfig.batchSysUseLogTrigger().getObject(),
sysUseLogBatchConfig.batchMenuUseHistoryTrigger().getObject()
); <5>
return schedulerFactoryBean;
}
@Bean
@ConfigurationProperties(prefix = "spring.quartz.datasource")
@QuartzDataSource <2>
public DataSource quartzDataSource() {
return DataSourceBuilder.create().type(HikariDataSource.class).build();
}
@Bean
@QuartzTransactionManager <3>
public DataSourceTransactionManager quartzTransactionManager() {
return new DataSourceTransactionManager(quartzDataSource());
}
----
<1> Spring Bean을 AutoWiring
<2> Job 정보를 조회, 저회 저장할 때 사용하는 Datasource (resources-dev, resources-prod/application.properties 참조)
<3> Quartz 전용 TransactionManager
<4> Quartz 설정 파일 세팅
<5> SchedulerFactoryBean 에서 실행 할 Trigger
== JobDetail 및 Trigger Bean 등록
아래는 KnoxSyncBatchConfig에 수행할 Job 클래스를 설정한 JobDetail Bean과 Trigger Bean을 등록한 예이다.
.KnoxSyncBatchConfig.java
[source, java]
----
@Value("${knox.approval.sync.cron}")
private String knoxApprovalSyncCron;
@Bean
public JobDetail knoxApprovalSyncJobDetail() {
return JobBuilder
.newJob(KnoxSyncBatchExecutor.class) <1>
.withIdentity("knoxApprovalSyncJob")
.withDescription("Knox Approval Sync Batch")
.storeDurably(true)
.build();
}
@Bean
public CronTriggerFactoryBean knoxApprovalSyncTrigger() {
CronTriggerFactoryBean approvalTrigger = new CronTriggerFactoryBean();
approvalTrigger.setJobDetail(knoxApprovalSyncJobDetail());
approvalTrigger.setCronExpression(knoxApprovalSyncCron);
return approvalTrigger;
}
----
<1> 배치잡 실행시 QuartzJobBean을 상속 받은 KnoxSyncBatchExecutor의 executeInternal 메소드가 실행된다.
== Job 구현
QuartzJobBean은 Spring Bean을 DI Job의 구현체에서 사용할 수 있도록 Job을 구현한 추상 클래스이며,
QuartzJobBean을 상속 받아 executeInternal 메소드를 오버라이드하여 실행할 배치 Service의 메소드를 호출한다.
.KnoxSyncBatchExecutor.java
[source,java]
----
@Component
@Log4j2
public class KnoxSyncBatchExecutor extends QuartzJobBean {
@Value("${node-id}")
private String nodeId;
@Autowired
private KnoxApprovalSyncService knoxApprovalSyncService;
@Override
protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
JobDetail jobDetail = jobExecutionContext.getJobDetail();
log.info(new StringBuilder()
.append("nodeId : ").append(nodeId).append(", ")
.append("jobName : ").append(jobDetail.getKey().getName()).append(", ")
.append("description : ").append(jobDetail.getDescription())
);
knoxApprovalSyncService.synchronizeKnox();
}
}
----
CAUTION: UserBatchExecutor의 경우 JobKey(JobBuilder.withIdentity 설정 값)를 이용해 분기 처리 하고 있으므로 Job 등록시 Name 명 지정에 주의하도록 한다.
.UserBatchConfig.java
[source, java]
----
/**
* 장기 미사용자 관리 Job
*/
@Bean
public JobDetail batchUserLongTermCheckJob() {
return JobBuilder
.newJob(UserBatchExecutor.class)
.withIdentity("batchUserLongTermCheck")
.withDescription("User LongTerm Check Batch")
.storeDurably(true)
.build();
}
/**
* 만료 권한 삭제 Job
*/
@Bean
public JobDetail batchUserAuthExpiredJob() {
return JobBuilder
.newJob(UserBatchExecutor.class)
.withIdentity("batchUserAuthExpired")
.withDescription("User Auth Expired Batch")
.storeDurably(true)
.build();
}
----
.UserBatchExecutor.java
[source, java]
----
@Override
protected void executeInternal(JobExecutionContext jobExecutionContext) {
JobDetail jobDetail = jobExecutionContext.getJobDetail();
String jobName = jobDetail.getKey().getName();
log.info(new StringBuilder()
.append("nodeId : ").append(nodeId).append(", ")
.append("jobName : ").append(jobName).append(", ")
.append("description : ").append(jobDetail.getDescription())
);
switch (jobName) {
case "batchUserLongTermCheck" -> userBatchService.execUserLongTermCheck();
case "batchUserAuthExpired" -> userBatchService.execUserAuthValidCheck();
case "batchUserAuthExpiredMailing" -> {
try {
userBatchService.execUserAuthValidAlarmMail();
} catch (IOException | MessagingException e) {
log.error(e.getMessage(), e);
}
}
default -> {
}
}
}
----