프로젝트 초기화
spring-boot-starter, spring-boot-starter-web, spring-boot-starter-data-jpa, spring-boot-starter-scheduling 등의 의존성 추가.Scheduling 활성화
@EnableScheduling 어노테이션을 추가하여 스케줄링 기능 활성화.
@SpringBootApplication
@EnableScheduling
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
Scheduled 메서드 정의
@Scheduled 어노테이션을 사용해 스케줄링 작업 정의.
cron, fixedRate, fixedDelay 등을 사용해 주기 설정.
@Service
public class ScheduledTasks {
@Scheduled(cron = "0 0 * * * ?") // 매 정각마다 실행
public void executeTask() {
System.out.println("Task executed at " + LocalDateTime.now());
}
@Scheduled(fixedRate = 5000) // 이전 작업 시작 시점부터 5초마다 실행
public void executeFixedRateTask() {
System.out.println("Fixed rate task executed at " + LocalDateTime.now());
}
@Scheduled(fixedDelay = 5000) // 이전 작업 종료 시점부터 5초 후에 실행
public void executeFixedDelayTask() {
System.out.println("Fixed delay task executed at " + LocalDateTime.now());
}
}
Thread Pool 설정
여러 작업이 동시에 실행될 수 있도록 ThreadPoolTaskScheduler 설정.
@Configuration
public class SchedulerConfig {
@Bean
public TaskScheduler taskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(10);
scheduler.setThreadNamePrefix("scheduled-task-pool-");
scheduler.initialize();
return scheduler;
}
}
작업의 동시 실행 방지
동기화 블록을 사용하여 작업의 중복 실행 방지.
@Service
public class ExclusiveTask {
private final Object lock = new Object();
@Scheduled(cron = "0 0/1 * * * ?")
public void executeExclusiveTask() {
synchronized (lock) {
System.out.println("Exclusive task executed at " + LocalDateTime.now());
// 작업 로직 수행
}
}
}
에러 핸들링
try-catch 블록 또는 AOP를 활용하여 스케줄 작업 중 발생하는 예외를 처리.
@Service
public class ErrorHandledTask {
@Scheduled(fixedRate = 60000)
public void executeTaskWithErrorHandling() {
try {
// 작업 수행
System.out.println("Error handled task executed");
} catch (Exception e) {
// 에러 로깅 및 처리
System.err.println("Task execution failed: " + e.getMessage());
}
}
}
작업 모니터링
@Configuration
public class DynamicSchedulerConfig implements SchedulingConfigurer {
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.addCronTask(() -> executeDynamicTask(), "0 0/5 * * * ?");
}
public void executeDynamicTask() {
System.out.println("Dynamic task executed at " + LocalDateTime.now());
}
}
UTC 시간 기준 사용
모든 서버가 UTC 시간대에서 동작하도록 설정.
cron 표현식을 UTC 시간 기준으로 설정.
@Scheduled(cron = "0 0 * * * ?", zone = "UTC")
public void executeTask() {
// 작업 수행 로직
}
Centralized Scheduler 사용
스케줄링 관리 시스템 도입
Quartz Scheduler 주요 개념
Quartz 설정
Quartz 의존성 추가 및 스케줄러 설정.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
@Configuration
public class QuartzConfig {
@Bean
public JobDetail jobDetail() {
return JobBuilder.newJob(MyJob.class)
.withIdentity("myJob")
.storeDurably()
.build();
}
@Bean
public Trigger trigger(JobDetail jobDetail) {
return TriggerBuilder.newTrigger()
.forJob(jobDetail)
.withIdentity("myJobTrigger")
.withSchedule(CronScheduleBuilder.cronSchedule("0 0/5 * * * ?")
.inTimeZone(TimeZone.getTimeZone("UTC")))
.build();
}
@Bean
public SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource) {
SchedulerFactoryBean factory = new SchedulerFactoryBean();
factory.setDataSource(dataSource);
factory.setJobDetails(jobDetail());
factory.setTriggers(trigger(jobDetail()));
factory.setOverwriteExistingJobs(true);
factory.setApplicationContextSchedulerContextKey("applicationContext");
return factory;
}
}
Quartz 클러스터링 설정
spring.quartz.job-store-type=jdbc
spring.quartz.jdbc.initialize-schema=always
spring.quartz.properties.org.quartz.jobStore.isClustered=true
spring.quartz.properties.org.quartz.jobStore.clusterCheckinInterval=20000
Spring Cloud Data Flow 주요 개념
Spring Cloud Data Flow 설정
Data Flow Server 설치 및 스케줄링 설정.
services:
dataflow-server:
image: springcloud/spring-cloud-dataflow-server:2.8.0
environment:
- SPRING_CLOUD_SCHEDULER_KUBERNETES_ENABLED=true
- SPRING_CLOUD_KUBERNETES_SECRETS_PATH=/etc/secrets
ports:
- "9393:9393"
cron expression을 이용하여 정해진 시간에 주기적으로 실행.작업 실패 기록 방법
데이터베이스 테이블을 이용한 실패 기록.
Job Execution Log와 Job Retry Queue 테이블 설계.
CREATE TABLE job_execution_log (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
job_name VARCHAR(255),
execution_time TIMESTAMP,
status VARCHAR(50),
error_message TEXT,
retry_count INT DEFAULT 0
);
CREATE TABLE job_retry_queue (
id BIGINT PRIMARY KEY,
job_name VARCHAR(255),
scheduled_time TIMESTAMP,
retry_count INT DEFAULT 0,
last_retry_time TIMESTAMP,
next_retry_time TIMESTAMP,
status VARCHAR(50)
);
재처리 로직
재시도 전략: 재시도 횟수를 제한하여 무한 재처리를 방지.
Job Retry Queue에 기록된 작업을 일정 주기마다 확인하여 재실행.
@Service
public class JobRetryService {
@Scheduled(fixedRate = 60000) // 1분마다 실행
public void retryFailedJobs() {
List<JobRetryQueue> pendingJobs = jobRetryQueueRepository.findPendingJobs(LocalDateTime.now());
for (JobRetryQueue job : pendingJobs) {
try {
performTask(job);
updateJobRetryStatus(job, "SUCCESS");
removeJobFromRetryQueue(job);
} catch (Exception e) {
handleRetryFailure(job, e.getMessage());
}
}
}
private void performTask(JobRetryQueue job) throws Exception {
// 작업 로직 수행
}
private void updateJobRetryStatus(JobRetryQueue job, String status) {
job.setStatus(status);
jobRetryQueueRepository.save(job);
}
private void removeJobFromRetryQueue(JobRetryQueue job) {
jobRetryQueueRepository.delete(job);
}
private void handleRetryFailure(JobRetryQueue job, String errorMessage) {
if (job.getRetryCount() >= maxRetryLimit) {
updateJobRetryStatus(job, "FAILED");
} else {
job.setRetryCount(job.getRetryCount() + 1);
job.setLastRetryTime(LocalDateTime.now());
job.setNextRetryTime(calculateNextRetryTime(job));
jobRetryQueueRepository.save(job);
}
}
private LocalDateTime calculateNextRetryTime(JobRetryQueue job) {
// 재시도 시간 계산 (예: 지수 백오프 방식)
return LocalDateTime.now().plusMinutes(5 * job.getRetryCount());
}
}
무한 반복 방지
N번까지만 시도.관리 인터페이스 제공
알림 설정