본문 바로가기

Spring

[Spring] 스프링 quartz 주기적으로 작업 실행하기

개발환경: spring3.1.2, tomcat7, java 1.7, mybatis

 


1. Quartz란?

Quartz는 Terracotta 라는 회사에 의해 개발된 Job Scheduling 라이브러리이다. 완전히 자바로 개발되어 어느 자바 프로그램에서도 쉽게 통합해서 개발이 가능하다. Quartz는 수십에서 수천 개의 작업도 실행 가능하며 간단한 interval 형식이나 Cron 표현식으로 복잡한 스케줄링도 지원한다.

 

2. Quartz 장단점

장점

  •  DB 기반으로 스케줄러 간의 Clustering 기능을 제공한다.
  •  시스템 Fail-over와 Random 방식의 로드 분산처리를 지원한다.
  •  n-memory Job Scheduler도 제공한다.
  •  여러 기본 Plug-in을 제공한다.
  •  ShutdownHookPlugin - JVM 종료 이벤트를 캐치해서 스케줄러에게 종료를 알려준다.
  •  LoggingJobHistoryPlugin - Job 실행에 대한 로그를 남겨 디버깅할 때 유용하게 사용할 수 있다.

단점

  •  Clustering 기능을 제공하지만, 단순한 random 방식이라서 완벽한 Cluster 간의 로드 분산은 안된다.
  •  어드민 UI을 제공하지 않는다.
  •  스케줄링 실행에 대한 History는 보관하지 않는다.
  •  Fixed Delay 타입을 보장하지 않으므로 추가 작업이 필요하다.

 

3. Quartz 구조

Job 인터페이스 - 실제 수행되는 execute 메소드를 명시합니다. 개발자는 해당 메소드를 구현한다.

  • JobDetail 인터페이스 - Job 구현 객체를 실행시키기 위한 정보를 정의한다.
    • JobClass - Job 구현 클래스
    • Description - Job 설명
    • JobDataMap - Job 실행시 필요한 정보들
  • Trigger 인터페이스 - Job 실행 조건을 정의한다.
  • Scheduler 인터페이스 - 등록된 Job과 Trigger를 관리하는 기능들을 정의한다.
  • JobListener 인터페이스 - Job 수행 전, 완료 이벤트와 중단 이벤트를 확인할 수 있는 기능을 정의한다.
  • JobStore 인터페이스 - Job, Trigger 정보를 저장하는 메커니즘을 정의한다. 메모리 혹은 데이터베이스를 사용한다.

 

4. Quartz 사용법

1. pom.xml 파일에 quartz 라이브러리의 의존성을 추가

<!-- quartz 라이브러러리 -->
<dependency>
	<groupId>org.quartz-scheduler</groupId>
	<artifactId>quartz</artifactId>
	<version>2.3.0</version>
</dependency>
		
<!-- 스프링 Quartz 도우미 라이브러리 -->
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-context-support</artifactId>
	<version>4.3.4.RELEASE</version>
</dependency>

 

2. 주기적으로 실행될 서비스 메소드를 생성

package com.research.spring.service;

public interface quartzService {
	
	public void time_reminder();
	
}

 

3. 위 interface의 구현 메소드를 추가한다. 

package com.research.spring.service.impl;

import java.text.SimpleDateFormat;
import java.util.Date;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;

import com.research.spring.dao.quartzDao;
import com.research.spring.service.quartzService;

@Service("quartzService")
@Repository
public class quartzServiceImpl implements quartzService{
	
	private static final Logger log = LoggerFactory.getLogger(quartzServiceImpl.class);
	
	@Autowired
	quartzDao quartzDao;

	@Override
	public void time_reminder() {
		
		try {
			
			Date nowDate = new Date();
			
			SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
			
			String nowStr = simpleDateFormat.format(nowDate);
			
			log.info("■■■■■■■■■■ " + nowStr);
			
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

}

 

4. root-context.xml 파일에 스케줄러 설정을 추가

    
    <context:component-scan base-package="com.research.spring" />
	
	<!-- 10초마다 현재시간 알려주는 트리거  -->
	<bean id="time_reminder" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
	    <!-- 서비스 구현 객체의 빈 이름을 인자로 지정 -->
	    <property name="targetObject" ref="quartzService" />
	    <!-- 서비스 객체에서 주기적으로 실행될 메소드를 지정 -->
	    <property name="targetMethod" value="time_reminder" />
	    <!-- 동시 실행을 방지 -->
	    <property name="concurrent" value="false" />
	</bean>
	
	<bean id="timeTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
	    <property name="jobDetail" ref="time_reminder" />
	    <property name="cronExpression" value="0/10 * * * * ?" />
	</bean>
	
	<!-- 스케줄러 -->
	<bean id="quartzJobScheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
	    <property name="triggers">
	        <!-- 앞에서 설정한 트리거를 등록. 필요하면 여러개 만들어서 등록할것... -->
	        <list>
	        	<ref bean="timeTrigger" />
	        </list>
	    </property>
	</bean>

 

4-1. 컴포넌트를 스캔할 패키지를 지정하는 코드. 프로젝트 구조 내 com.research.spring 하위 요소는 전부 스캔한다는 의미이다. 

<context:component-scan base-package="com.research.spring" />

프로젝트 구조

 

4-2. 주기적으로 작업을 실행할 트리거 Bean을 생성한다.

- targetObject에 3번에서 @Service("quartzService") 어노테이션으로 선언한 이름을 적는다.

- targetMethod에 3번에서 구현한 메소드명을 적는다.

 

4-3. 트리거의 작동 시간을 설정하는 Bean을 생성한다.

- jobDetail4-2에서 선언한 bean id를 적는다.

- (corn표현식 사용) cronExpression실행될 시간을 적는다.

	<!-- 10초마다 현재시간 알려주는 트리거  -->
	<bean id="time_reminder" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
	    <!-- 서비스 구현 객체의 빈 이름을 인자로 지정 -->
	    <property name="targetObject" ref="quartzService" />
	    <!-- 서비스 객체에서 주기적으로 실행될 메소드를 지정 -->
	    <property name="targetMethod" value="time_reminder" />
	    <!-- 동시 실행을 방지 -->
	    <property name="concurrent" value="false" />
	</bean>
	
	<bean id="timeTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
	    <property name="jobDetail" ref="time_reminder" />
	    <property name="cronExpression" value="0/10 * * * * ?" />
	</bean>

 

4-4. 만든 트리거를 등록한다.

여러개의 트리거를 사용하고 싶으면 <list></list>안에 추가하면 된다.

<!-- 스케줄러 -->
	<bean id="quartzJobScheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
	    <property name="triggers">
	        <!-- 앞에서 설정한 트리거를 등록. 필요하면 여러개 만들어서 등록할것... -->
	        <list>
	        	<ref bean="timeTrigger" />
	        </list>
	    </property>
	</bean>

 

5. 실행 결과

정상적으로 실행되고 있다. time_reminder()로 구현한 10초마다 현재시간 찍는 로그가 보인다.

 

 

 

※ Corn 표현식

 순서 필드명 사용 가능한 값
 1  seconds  0~59 , - * /
 2  minutes  0~59 , - * /
 3  hours  0~23 , - * /
 4  day of month  1~31 , - * ? / L W
 5  month  1~12 or JAN-DEC , - * /
 6  day of week  1-7 or SUN-SAT , - * ? / L #
 7  years (optional)  1970~2099 , - * /

특수문자의 의미

 기호 의미   사용 예
 *  모든 수를 의미 seconds에서 사용하면 매초, minutes에서 사용하면 매분, hours에서 사용하면 매시간 
 ?  해당 항목을 사용하지 않음 day of month에서 사용하면 월중 날짜를 지정하지 않음. day of week에서 사용하면 주중 요일을 지정하지 않음 
 -  기간을 설정 hours에서 10-12이면 10시, 11시, 12시에 동작
minutes에서 58-59이면 58분, 59분에 동작
 , 특정 시간을 지정 day of week에서 2,4,6이면 월,수,금에만 동작함
 / 시작시간과 반복 간격 설정 seconds위치에 0/15로 설정하면 0초에 시작해서 15초 간격으로 동작 
minutes위치에 5/10으로 설정하면 5분에 시작해서 10분 간격으로 동작
 L 마지막 기간에 동작
day of month, day of week에서만 사용
day of month에서 사용하면 해당월 마지막 날에 수행
day of week에서 사용하면 토요일에 수행
 W 가장 가까운 평일 동작
day of month에만 사용 
15W로 설정하면 15일이 토요일이면 가장 가까운 14일 금요일에 실행
15W로 설정하고 15일이 일요일이면 16일에 실행
15W로 설정하고 15일이 평일이면 15일에 실행
 LW L과 W의 조합  그달의 마지막 평일에 동작 
 # 몇 번째 주와 요일 설정
day of week에 사용 
6#3이면 3 번째 주 금요일에 동작 
4#2이면 2번째 주 수요일에 동작

사용 예시

표현식 의미 
 0 0 12 * * * 매일 12시에 실행 
 0 15 10 * * * 매일 10시 15분에 실행 
 0 * 14 * * * 매일 14시에 0분~59분까지 매분 실행
 0 0/5 14 * * * 매일 14시에 시작해서 5분 간격으로 실행 
 0 0/5 14,18 * * * 매일 14시, 18시에 시작해서 5분 간격으로 실행 
 0 0-5 14 * * * 매일 14시에 0분, 1분, 2분, 3분, 4분, 5분에 실행 
 0 0 20 ? * MON-FRI 월~금일 20시 0분 0초에 실행 
 0 0/5 14 * * ? 아무요일, 매월, 매일 14:00부터 14:05분까지 매분 0초 실행 (6번 실행됨)
 0 15 10 ? * 6L  매월 마지막 금요일 아무날이나 10:15:00에 실행
 0 15 10 15 * ? 아무요일, 매월 15일 10:15:00에 실행 
 * /1 * * * * 매 1분마다 실행
 * /10 * * * * 매 10분마다 실행 

http://www.cronmaker.com/?0 왼쪽의 사이트를 이용해서 편하게 생성할 수도 있다. (크론 표현식 생성기)

 

 

※ 본문의 예제 소스

 

research_spring.zip
0.10MB