幸福伝説

Quartz 본문

Tool/Quartz

Quartz

행복전설 2015. 9. 16. 14:12

 

[Spring] 스프링3, 쿼츠1.8.6 스케줄링(SpringFramework3, Quartz 1.8.6 Job Schedule),

스프링에서 작업스케줄링하기

 

 

스케줄링이란 일정한 시간마다 특정한 업무를 수행하는 것이다.

오라클데이터베이스 같은 경우에는 dbms_job에서 지원하며 자바기반 환경에서는 quartz 를 많이 사용한다.

 

quartz site : http://quartz-scheduler.org/

#Injection Diagram

 

다양한 Quartz 컴포넌트들이 존재하지만 Spring에서 사용할때 핵심 컴포넌트는 아래와 같다.

 

MyJob 컴포넌트를 JobDetailBeanInjection 하고,

JobDetailBeanTriggerBeanIngection 을 합니다.

최종적으로 TriggerBeanSchdu​lerBeanIngection을 함으로써 하나의 Job에 대한 스케줄러 설정이 끝나게 됩니다.

 

MyJob : 개발자가 직접 작성할 비즈니스 컴포넌트, Job 로직을 구현.

JobDetailBean : Job, Job속성 및 파라미터 정보를 aggregate하는 컴포넌트

TriggerBean : Job 실행시작, 주기 등을 설정하는 컴포넌트

SchedulerBean : 모든 Job 설정 정보를 갖고 Manager Thread를 생성해서 해당 시간에 Job를 실행하는 컴포넌트

 

 

이번 예제에서는 Spring Framework3.2와 quartz 1.8.6에서 간단히 스케줄링을 구현했다

* 앞으로 구현 할 예제(예제 : 스프링 지원 기능을 이용한 Quartz 사용) 의 작성 순서.

 

1. 간단히 스프링 프로젝트 하나 생성하자.

2. 관련 라이브러리 ( *.jar) 준비 ( 메이븐 사용자라면 pom.xml 에 Dependency 추가 )

3. 실제 스케줄링 되면서 실행될 태스크 클래스와 실행메소드를 작성 ( LogProcessor.java )

4. 쿼츠에서 실행될 JOB을 정의. (JOB에 TASK를 정의한다.)

- MethodInvokingJobDetailFactoryBean을 이용하는 간단방법

=> 특정 객체의 단일 메서드를 호출하는 잡을 정의할수 있다. 이 빈을 사용하면 잡을 생성하는 시간이 많이 줄어든다.

- QuartzJobBean을 상속한 클래스를 만들고 XML에서 쿼츠 JOB을 정의하는 방법

5. Trigger 기술(정의한 JOB을 어떤 주기로 실행할 것인지 정의)

6. 스케줄러 관리를 위한 Scheduler Factory를 정의

7. 실행.

 

주의. Spring Framework3.2에서 아직 Quartz2.0 이상은 지원하지 않는 것 같다. 다음 같은 오류 발생!!

class org.springframework.scheduling.quartz.JobDetailBean has interface org.quartz.JobDetail as super class

http://www.mkyong.com/spring/spring-quartz-scheduler-example/

 

참고. Cron 표현식.

 

The Unix cron expression is highly flexible and powerful, read more in following websites :

1.http://en.wikipedia.org/wiki/CRON_expression

2.http://www.quartz-scheduler.org/docs/examples/Example3.html

Cron 표현식은 공백으로 구분된 일곱개의 필드로 설정된다.(마지막 필드는 선택사항)

와일드카드 (*는 모든 값에 대응), 물음표(?는 "일"과 "요일" 필드 중 하나만 지정할 때 사용하며 동시에 둘다 사용할 수는 없다)

위치

필드명

값의 번위

1

0~59

2

0~59

3

0~23

4

1~31

5

1~12 또는 JAN-DEC

6

요일

0-7 또는 SUN-SAT (0 또는 7은 일요일)

7

(선택사항)

1970-2099

[참고]

* : 전체 값을 의미

? : 물음표(?는 "일"과 "요일" 필드 중 하나만 지정할 때 사용하며 동시에 둘다 사용할 수는 없다)

특정 값 : 해당 시간을 정확하게 지정. 예) "0", "10", "20"
값1-값2 : 값1 부터 값 2 사이를 표현. 예) "0-10"
값1, 값2, 값3 : 콤마로 구분하여 특정 값 목록을 지정. 예) "0, 15, 30"
범위/숫자 : 범위에 속한 값 중 숫자 만큼 간격으로 값 목록을 지정. 예를 들어 , "0-23/2"는 0부터 23까지 2간격으로 값을 설정.

("0 0 * * * *") : 매일 매시 정각
("*/10 * * * * *") : 0, 10, 20, 30, 40, 50 초
("0 0 8-10 * * *") : 매일 8시, 9시, 10시 정각
("0 0/30 8-10 * * *") : 매일 8시, 8시 30분, 9시, 9시 30분, 10시
("0 0 9-18 * * 1-5") : 매주 월요일부터 금요일의 9시부터 오후 6시까지 매시

[코드]

File : pom.xml [메이븐 사용자를 위한 라이브러리 추가]

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
...
<dependencies>

<!-- Spring 3 dependencies -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>3.1.2.RELEASE</version>
</dependency>

<!-- QuartzJobBean in spring-context-support.jar -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>3.1.2.RELEASE</version>
</dependency>

<!-- Spring + Quartz need transaction -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>3.1.2.RELEASE</version>
</dependency>

<!-- Quartz framework -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>1.8.6</version>
</dependency>

</dependencies>
...

 

예제 : 스프링 지원 기능을 이용한 Quartz 사용

[코드]

applicationContext.xml - Spring Bean Configuration File

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:task="http://www.springframework.org/schema/task"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd"
>


<!-- 실제 작업을 하는 클래스 -->
<bean id="logProcessor" class="com.spring.exam.LogProcessor" />

<!-- Spring Quartz -->
<!-- 실제 스케줄링될 태스크(세터주입될)와 JOB을정의한 클래스등록 -->
<bean name="runMeJob" class="org.springframework.scheduling.quartz.JobDetailBean">
<!-- JOB을 정의 -->
<property name="jobClass" value="com.spring.exam.RunMeJob" />
<!-- 실제 스케줄링될 JOB -->
<property name="jobDataAsMap">
<map>
<entry key="logProcessor" value-ref="logProcessor" />
</map>
</property>
</bean>
<!--
위에서 정의한 JobDetailBean을 이용하여 스케줄링되는 JOB을 등록하는 방법이외,
JOB을 등록하는 다른 방법
MethodInvokingJobDetailFactoryBean을 이용하는 간단한 방법, 스케줄링될 클래스와 메소드 정의
-->

<!--
<bean id="runMeJob" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="logProcessor" />
<property name="targetMethod" value="process" />
</bean>
-->


<!-- Trigger -->
<!-- 정의한 작업(JOB)을 어떤 주기로 실행할 것인지 정의 -->

<!-- Simple Trigger, run every 5 seconds -->
<bean id="simpleTrigger"
class="org.springframework.scheduling.quartz.SimpleTriggerBean">


<property name="jobDetail" ref="runMeJob" /> <!-- 실행할 작업 -->
<property name="repeatInterval" value="5000" /> <!-- 반복 주기 -->
<property name="startDelay" value="1000" /> <!-- 최초 시작시 지정한 시간 후에 시작 -->

</bean>

<!-- Cron Trigger, run every 5 seconds -->
<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">

<property name="jobDetail" ref="runMeJob" /> <!-- 실행할 작업 -->
<property name="cronExpression" value="0/5 * * * * ?" /> <!-- 초 분 시 월 요일 년-->

</bean>

<!-- Scheduler Factory -->
<!-- 스케줄러 관리를 위한 Scheduler Factory를 정의, JOB과 trigger 함께 기술 -->
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="jobDetails">
<list>
<ref bean="runMeJob" />
</list>
</property>

<property name="triggers">
<list>
<ref bean="simpleTrigger" />
</list>
</property>
</bean>

</beans>

LogProcessor.java

Colored By Color Scripter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.spring.exam;

import java.text.SimpleDateFormat;
import java.util.Calendar;

/**
* 실제 스케줄링 되면서 실행될 태스크 클래스와 실행 메서드
*/


public class LogProcessor {

public void process() {
Calendar nowTime = Calendar.getInstance();
SimpleDateFormat sd = new SimpleDateFormat("[ yyyy-MM-dd HH:mm:ss ]");
String strNowTime = sd.format(nowTime.getTime());

System.out.println(strNowTime + ": 로그 처리");
}
}

RunMeJob.java

Colored By Color Scripter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.spring.exam;

import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;

//QuartzJobBean은 복잡한스케줄링에 적합한 유연한 스케줄러.
//상속받아서 스케줄러를 구현.
public class RunMeJob extends QuartzJobBean {

//실제 실행될 태스크
private LogProcessor logProcessor;

//실제 실행될 태스크를 setter방식으로 주입
public void setLogProcessor(LogProcessor logProcessor) {
this.logProcessor = logProcessor;
}

@Override //실행을 원하는 메서드 호출
protected void executeInternal(JobExecutionContext context)
throws JobExecutionException {
logProcessor.process();
}
}

Main.java

Colored By Color Scripter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.spring.exam;

import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;


public class Main {

public static void main(String[] args) {

AbstractApplicationContext context
= new ClassPathXmlApplicationContext("applicationContext.xml");

//context.close();
}
}



 

 

 

예제 : Quartz의 직접 사용.


[코드]

applicationContextExam01.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:task="http://www.springframework.org/schema/task"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd"
>


<!-- 실제 작업을 하는 클래스 빈 등록->
<bean id="logProcessor" class="com.spring.exam02.LogProcessor" />

</beans>

RunMeJob.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.spring.exam02;

import java.text.SimpleDateFormat;
import java.util.Calendar;

/**
* 실제 스케줄링 되면서 실행될 태스크 클래스와 실행 메서드
*/


public class LogProcessor {

public void process() {
Calendar nowTime = Calendar.getInstance();
SimpleDateFormat sd = new SimpleDateFormat("[ yyyy-MM-dd HH:mm:ss ]");
String strNowTime = sd.format(nowTime.getTime());

System.out.println(strNowTime + ": 로그 처리");
}
}

LogProcessor.java

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
package com.spring.exam01;

import java.util.Map;

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

import com.spring.exam02.LogProcessor;

/*
* Job 인터페이스를 구현한 잡을 작성
* 예제에는 JobExecutionContext 객체를 전달 받아 잡 데이터 맵을 얻고
* 로그를 처리하는 애플리케이션의 process() 메서드를 실행하는 잡을 나타낸다.
*/

public class RunMeJob implements Job{

@Override
public void execute(JobExecutionContext context)
throws JobExecutionException {

Map dataMap = context.getJobDetail().getJobDataMap();
LogProcessor logProcessor = (LogProcessor) dataMap.get("logProcessor");

try {
logProcessor.process(); //실직적인 작업을 하는 메서드 호출
} catch (Exception e) {
throw new JobExecutionException(e);
}

}

}

Main.java

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
package com.spring.exam01;

import java.util.Date;
import java.util.Map;

import org.quartz.CronTrigger;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SimpleTrigger;
import org.quartz.impl.StdSchedulerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.spring.exam02.LogProcessor;


//잡(RunMeJob.java)을 작성하고 나면
//Quartz API를 사용해서 잡을 설정하고 스케줄링한다.
//아래 예제는, logProcessor를 최초 실행할때 5초동안 실행을 지연한 후 10간격으로 반복실행
public class Main {
public static void main(String[] args) throws SchedulerException {
ApplicationContext context
= new ClassPathXmlApplicationContext("applicationContextExam01.xml");


//JobDetail 객체에 잡에 대한 상세 정보를 설정하고
//jobDataMap프로퍼티에 잡 데이터를 추가, 그리고 스케줄링 프로퍼티를 설정하기위해
//SimpleTrigger 객체를 생성하고 이 트리거를 통해 잡을 실행할 스케줄러를 생성.

LogProcessor logProcessor = (LogProcessor) context.getBean("logProcessor");

JobDetail job = new JobDetail();
job.setName("runMeJob");
job.setJobClass(RunMeJob.class);
Map dataMap = job.getJobDataMap();
dataMap.put("logProcessor", logProcessor);

//SimpleTrigger
//시작시각, 종료시각, 반복주기, 반복횟수와 같은 기본적인 트리거 프로퍼티를 설정
SimpleTrigger trigger = new SimpleTrigger();
trigger.setName("simpleTrigger");
trigger.setStartTime(new Date(System.currentTimeMillis()+5000)); // 5초후 시작
trigger.setRepeatCount(SimpleTrigger.REPEAT_INDEFINITELY);
trigger.setRepeatInterval(10000); //10초마다 실행

//CronTrigger
//유닉스 cron표현식으로 잡의 실행 시간을 지정
/*
CronTrigger trigger = new CronTrigger();
trigger.setName("cronTrigger");
trigger.setCronExpression("0 55 * * * ?"); //초 분 시 일 월 요일 년(선택사항)
*/

Scheduler scheduler = new StdSchedulerFactory().getScheduler();
scheduler.start();
scheduler.scheduleJob(job,trigger);

}
}