본문 바로가기

Programing/Spring

[Spring] 배치, 스케쥴러 사용 : Quartz

프로젝트에서 매번 등장하는 배치스케쥴러, 이를 구현하기 위한 다양한 방법이 존재하지만 자바 스케쥴러 개발에 사용하는 오픈소스 라이브러리인 Quartz Scheduler를 이용하는 방법을 알아보도록 하자.

 

배치 프로그램에 대한 자세한 정보는 아래 블로그 참조!

https://limkydev.tistory.com/140

 

 

 

1. Maven Dependency

  • maven 환경에서 외부 라이브러리를 사용하기 위해서 pom.xml 에 Quartz Library를 dependency 한다.
1
2
3
4
5
6
7
8
9
10
11
12
<!-- https://mvnrepository.com/artifact/org.quartz-scheduler/quartz -->
<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
</dependency>
 
 
<!-- https://mvnrepository.com/artifact/org.quartz-scheduler/quartz-jobs -->
<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz-jobs</artifactId>
</dependency>
cs

 

 

 

2. Job Code 작성

  • Quartz Scheduler 는 기능을 수행하는 단위인 Job과 스케줄에 대한 정보를 가진 Trigger를 스케쥴러에 연결하여 실행하는 구조이다. 먼저 스케줄에 따라 동작할 Job 클래스를 작성한다.
  • 이때 Job Code는 quartz.Job Interface 를 implements 하여 구현하게 되는데, 해당 interface의 execute() method를 상속받아 수행될 로직을 작성하도록 한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package com.acma.quartzScheduler.job;
 
import java.text.SimpleDateFormat;
import java.util.Date;
 
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
 
import lombok.extern.slf4j.Slf4j;
 
 
/*
 * Job Interface를 implemnets 하여 구현한다.
 */
@Slf4j
public class acmaQuartzJob implements Job {
 
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        
        Date date = new Date();
        SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy년 MM월 dd일");
        SimpleDateFormat sdf2 = new SimpleDateFormat("HH시 mm분 ss초");
        String currentDate = sdf1.format(date);
        String currentTime = sdf2.format(date);
        
        /*
         *         execute() method 에 로직 추가
         */
        log.info("========= acmaQuartzJob execute() method Start !!! =========");
        log.info("Start Time >>> {}", currentDate + " " + currentTime);
    }
 
}
cs

 

 

 

3. Scheduler Code 작성

  • 스케쥴에 따라 작성한 Job을 수행하는 코드를 Quartz Library를 사용하여 작성한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package com.acma.quartzScheduler.scheduler;
 
import java.util.Date;
 
import javax.annotation.PostConstruct;
 
import org.quartz.CronScheduleBuilder;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;
import org.springframework.stereotype.Component;
 
import com.acma.quartzScheduler.job.acmaQuartzJob;
 
 
@Component
public class aCmaQuartzScheduler {
    
    private SchedulerFactory schedulerFactory;
    private Scheduler scheduler;
 
 
    @PostConstruct
    public void start() throws SchedulerException {
 
        schedulerFactory = new StdSchedulerFactory();
        scheduler = schedulerFactory.getScheduler();
        scheduler.start();
 
        JobDetail job = JobBuilder.newJob(acmaQuartzJob.class).withIdentity("aCmaJob").build();
        Trigger trigger = TriggerBuilder.newTrigger()
                            .withSchedule(CronScheduleBuilder.cronSchedule("30 * * * * ?"))
                            .build();
 
        //Trigger trigger = TriggerBuilder.newTrigger().startAt(startDateTime).endAt(endDateTime)
        //        .withSchedule(CronScheduleBuilder.cronSchedule("*/1 * * * *")).build();
 
        scheduler.scheduleJob(job, trigger);
    }
}
cs

 

  • 21 Line : Spring bean 으로 등록하기 위해 Class에 @Component annotation를 선언한다.
  • 28 Line : 실제 스케쥴러를 구현한 start() method 에 @postContruct annotation을 선언하여 해당 클래스가 인스턴스화 되자마자 자동으로 동작하도록 한다. 즉 Class 가 인스턴스화 되자마자 start() method 가 동작하게 되며, 스케쥴러를 통한 배치 Job은 사용자의 동작 없이 자동으로 수행하게 하기 위한 로직이므로 어딘가에서 method를 호출하여 실행하기보다는 자동으로 로직이 수행되도록 구현하여야 한다.
  • 31 Line : quartz.SchedulerFactory 를 선언한다.
  • 32 Line : quartz.Scheduler 를 quartz.SchedulerFactory Class의 getScheduler() method를 통해 지정해준다.
  • 33 Line : quartz.Scheduler 를 .start() 해주는 것으로 스케쥴러를 시작하겠다는 명령을 내리게 된다.
  • 35 Line : 작성한 Job을 지정한다. 이때 identity 는 해당 Job을 구분하는 고유 명을 지정하도록 한다. 같은 Job 로직이라도 서로 다른 스케줄로 동작하게 할 경우가 있기 때문에 각각의 Job 은 고유한 identity를 가져야 한다.
  • 36 Line : Trigger 를 생성한다. Trigger는 TriggerBuilder Class를 사용하여 구현하며, 스케줄러를 수행할 스케쥴 정보를 담고 있다. 이때 Cron 문법을 사용하여 스케줄을 지정하는 방법을 주로 사용한다.
  • 38 Line : startAt() 과 endAt()을 사용하여 Job 스케줄의 시작, 종료 시간을 지정할 수 있다.
  • 41 Line : quartz.Scheduler 에 Job 과 Trigger를 연결한다. Job과 Trigger를 여러 개 만들어 각각 quartz.Scheduler 에 지정해주면 여러 개의 Job 스케줄이 동시에 작동하게 된다.

 

 

 

4. Scheduler 실행

  • Spring Boot 의 main method()가 있는 application Class에서 작성한 스케쥴러를 Autowired 하여 서버 시작과 동시에 자동으로 동작하게 구현한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.acma.quartzScheduler;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
 
import com.acma.quartzScheduler.scheduler.aCmaQuartzScheduler;
 
@SpringBootApplication
public class QuartzSchedulerApplication {
    
    @SuppressWarnings("unused")
    @Autowired
    private aCmaQuartzScheduler scheduler;
 
    public static void main(String[] args) {
        SpringApplication.run(QuartzSchedulerApplication.class, args);
    }
 
}
cs

 

 

 

5. 결과

  • 서버 구동 후 Console 창에 미리 작성한 로그가 출력되는 것을 확인한다.

매 분 30초마다 미리 작성한 로그가 출력되고 있다.

 

 


 

 

 

※ Cron Expression : 총 7개의 필드가 있고 마지막 필드(년도)는 생략가능하다.

필드이름 허용 값
초(Seconds) 0 ~ 59
분(Minutes) 0 ~ 59
시간(Hours) 0 ~ 23
일(Day-of-month) 1 ~ 31
달(Months) 1 ~ 12 or JAN ~ DEC
요일(Day-of-week) 1 ~ 7 or SUN-SAT
년도(Year)(생략가능) 1970 ~ 2099

 

 

 

※ Cron Expression의 특수문자

Expr 설명
* 모든 수를 나타낸다.
- 값의 사이를 의미한다.
"* 10-13 * * * *" : 10, 11, 12, 13분에 동작한다.
, 특정 값을 지칭한다.
"* 10,11,13 * * * *" : 10, 11, 13분에 동작한다.
/ 값의 증가를 표현한다.
"* 0/5 * * * *" : 0분부터 시작해서 5분마다 동작한다.
? 특별한 값이 없음을 나타낸다.
(day-of-month, (day-of-month, day-of-week 필드만 사용) 일, 요일에 하나만 설정할 때 나머지에 지정한다.
L 마지막 날을 나타낸다.
(day-of-month, day-of-week 필드만 사용) 
일 필드에 사용되면 이달의 마지막일을 나타낸다. L-3 은 이달의 마지막 날 3일 전부터 마지막 날까지를 나타낸다.
요일 필드에 사용되면 토요일(7 or SAT)을 나타낸다. 6L or FRIL 은 이달의 마지막 금요일을 나타낸다.
W 주어진 날로부터 가장 가까운 평일(월 - 금)을 나타낸다.
15W를 일 필드에 사용하면 이달의 15번째 날에서 가장 가까운 평일을 나타낸다.
# 이달의 n번째 x요일을 나타낸다.
6#3 or FRI#3 은 이달의 세 번째 금요일을 나타낸다.

 

 

 

※ Cron Expression 예제

Question Answer
매 5분마다 실행 "0 0/5 * ?"
20초뒤 5분마다 실행 "20 0/5 * ?"
매일 오전 3시에 실행 "0 0 3 * * ?"
매주 일요일 오전 3시에 실행 "0 0 3 ? * SUN"
매주 월요일과 수요일 13:30, 14:30, 15:30, 16:30에 실행 "0 30 13-16 ? * MON,WED"
매월 3일, 15일 오전 3시부터 오전 6시 사이에 15분 간격으로 실행 "0 0/15 3-5 3,15 * ?"

 

quartzScheduler.zip
0.05MB

 

 

 

 

"1년 차 경력으로 10년을 일하는 개발자가 되지 말라!"는 나의 유일한 보스의 말씀.

오늘도 노력하는 개발자가 되기를 다짐하며...