Spring 4.x + Quartz 2.x 연동 방법 (Spring 4.x + Quartz 2.x Example)

quartzScheduler

2014년 첫 포스팅 입니다.🙂
여러분 모두 새해 복 많이 받으시길 바라며, 모두 대박 나시길 바랍니다.
또한 올해 부터는 좀 더 읽기 편하도록 모바일 환경에
맞는 테마로 수정을 하였습니다.

오늘 포스팅은 “Spring 4 + Quartz 2.x” 연동시 몇 가지 주의 사항에 대해서
말씀 드리려고 합니다.
※ 기회가 된다면 Spring 4.x에 대한 아키텍쳐적인 견해를 포스팅 할 예정입니다.

Quartz“는 “Crontab” 또는 “Jenkins” 같이 어플리케이션  내부에 임베디드 시킬수
있는  “스케줄러 자바 프레임웍” 입니다.

#Jenkins VS Quartz

예전에는 대부분 “Batch Job” 또는 일정한 “Job“를 실행 하기 위해서
Crontab“를 스케줄러로 많이 사용을 했습니다.
하지만 최근에는 “Jenkins” (http://jenkins-ci.org/)가 많이 알려지면서
점점 더 사용률이 올라가는 추세 입니다.

가끔 “Jenkins“로 하면 될 걸 굳이 “Quartz“를 사용하냐고 물어 보실때가 있습니다.
틀린말은 아니지만 각 상황에 맞춰서 “trade-off” 할 필요가 있습니다.

Jenkins의 장점

  • Job 추가/삭제/목록 등을 UI를 통해서 쉽게 관리 가능
  • Job 순서에 대한 개런티를 보장함 (즉 선행된 동일 Job이 끝날때 까지 대기)
  • 다양한 플러그인 제공
  • 클러스터링을 통한 중앙 집중 관리
  • 심플한 Job Flow를 제공
  • 실행 Job 앞뒤로 before, after 처리 가능
  • Java 외에 모든 언어 및 shell를 지원
  • Job 실패 와 성공 유무를 UI를 통해서 확인 가능
  • 실행 중인 Job를 취소 할 수 있음.
  • Job 시작/종료 시간을 알 수 있음.

Jenkins의 단점

  • 세밀한 스케줄러 타이밍 어려움
    매번 Job를 hard하게  run (java -jar ….)하기 때문에
    (스프링으로 된 Job일 경우 10 ~ 15초 정도 delay)
    초 단위 Batch를 정확하게 스케줄링 하기 어려움
  • 비즈니스 로직에 의한 스케줄러 실행 유무가 어려움

참고 사이트 : http://geekery.freecharge.in/2013/04/19/3/

저 같은 경우 예전에 “Quartz“를 사용한 경우가 많았는데 최근에는
초 ~ 5분단위가 아닌 나머지 Batch Job 같은 경우는 “Jenkins“를
사용하고 “미세한 Job” 또는 “비즈니스 컴포넌트” 와 연동이 필요한
경우는 “Quartz“를 사용하고 있습니다.

#Application Stack

아래는 Spring + Quart를 사용할때의 Application Stack 입니다.

이미지 4

“Quartz”는 “Tomcat” 같이 “Stand Alone” 형태의 서버가 아니고 일반 자바 라이브러로
되어 있습니다. 그래서 제공 하는 API 오브젝트를 생성해서 사용을 하면
내부적으로 생성된 쓰레드들에 의해서 관리가 됩니다.

#Injection Diagram

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

이미지 5

MyJob” 컴포넌트를  “JobDetailBean“에 Injection 하고,
JobDetailBean“은 “TriggerBean“에 Injection을 합니다.
최종적으로 “TriggerBean“을  “SchdulerBean“에 Injection을 함으로써
하나의 Job에 대한 스케줄러 설정이 끝나게 됩니다.

  • MyJob : 개발자가 직접 작성할 비즈니스 컴포넌트 입니다.
    즉, Job 로직을 구현합니다.
  • JobDetailBean : Job, Job 속성 및 파라미터 정보를 aggregate하는 컴포넌트 입니다.
  • TriggerBean : Job 실행 시작, 주기등을 설정하는 컴포넌트 입니다.
  • SchedulerBean : 모든 Job 설정 정보를 갖고 Manager Thread를 생성해서
    해당 시간에 Job를 실행하는 컴포넌트 입니다.

#Spring 4.x + Quartz 1.8.x

Spring 3.x 이상 + Quartz를 연동 할때 가장 쉽고
머리 안아프는 방법은 “Quartz 1.8.x를 사용” 하는 것입니다.
대부분 구글링을 하면 나오는 예제가 바로 위와 같은 조합 입니다.

제가 테스트 했던 tool version 입니다.

  • JDK 1.6
  • Spring 4.0
  • Quartz 1.8.2
  • Maven 3.x

pom.xml

	<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
			<version>4.0.0.RELEASE</version>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context-support</artifactId>
			<version>4.0.0.RELEASE</version>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-tx</artifactId>
			<version>4.0.0.RELEASE</version>
		</dependency>

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

MyJob

public class DummyTask {

    public void print() {
        System.out.println("Spring 4.0 + Quartz 2.2 ");        
    }

}

JobDetailBean

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


public class DummyJobBean extends QuartzJobBean {

    private DummyTask dummyTask;

    protected void executeInternal(JobExecutionContext context)
            throws JobExecutionException {

        dummyTask.print();

    }


    public void setDummyTask(DummyTask dummyTask) {
        this.dummyTask = dummyTask;
    }

}

Spring XML

<?xml version="1.0" encoding="UTF-8"?>

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


    <bean id="dummyTask" class="pe.beyondj2ee.quartz.DummyTask"/>

    <bean name="dummyJob" class="org.springframework.scheduling.quartz.JobDetailBean"
          p:jobClass="pe.beyondj2ee.quartz.DummyJobBean">
        <property name="jobDataAsMap">
            <map>
                <entry key="dummyTask" value-ref="dummyTask"/>
            </map>
        </property>
    </bean>

    <bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean"
          p:jobDetail-ref="dummyJob"
          p:startDelay="1000"
          p:cronExpression="0/3 * * * * ?"/>

    <bean id="schedulerFactoryBean" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <property name="triggers">
            <list>
                <ref bean="cronTrigger"/>
            </list>
        </property>
    </bean>

</beans>

이렇게 설정을 하면 3초 간격으로
“Spring 4.0 + Quartz 1.8.6” 라는 메세지를 출력 합니다.

이미지 2

Bootstrap

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Bootstrap {
    
    public static void main (String[] args) throws Exception {

        new ClassPathXmlApplicationContext("quartz2.2.1.xml");

    }

}

#Spring 4.x + Quartz 2.8.x

“Spring 4.x + Quartz 1.8.x” 와 차이점은 거의 없습니다.
단지 pom.xml 과 Spring XML 부분만 다르게 설정 하시면 됩니다.

pom.xml

	<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
			<version>4.0.0.RELEASE</version>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context-support</artifactId>
			<version>4.0.0.RELEASE</version>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-tx</artifactId>
			<version>4.0.0.RELEASE</version>
		</dependency>

		<dependency>
			<groupId>org.quartz-scheduler</groupId>
			<artifactId>quartz</artifactId>
			<version>2.2.1</version>
		</dependency>

※ Quartz 버전을 “2.2.1”로 수정을 합니다.

Spring XML

<?xml version="1.0" encoding="UTF-8"?>

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


    <bean id="dummyTask" class="pe.beyondj2ee.quartz.DummyTask"/>

    <bean name="dummyJob" class="org.springframework.scheduling.quartz.JobDetailFactoryBean"
          p:jobClass="pe.beyondj2ee.quartz.DummyJobBean"
          p:durability="true">
        <property name="jobDataAsMap">
            <map>
                <entry key="dummyTask" value-ref="dummyTask"/>
            </map>
        </property>
    </bean>

  <!--<bean id="simpleTrigger"-->
          <!--class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean"-->
            <!--p:jobDetail-ref="dummyJob"-->
            <!--p:repeatInterval="3000"-->
            <!--p:startDelay="1000"/>-->
            
    <bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean"
          p:jobDetail-ref="dummyJob"
          p:startDelay="1000"
          p:cronExpression="0/3 * * * * ?"/>

    <bean id="schedulerFactoryBean" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <property name="triggers">
            <list>
                <ref bean="cronTrigger"/>
            </list>
        </property>
    </bean>

</beans>

  • JobDetailBean ” 클래스 대신  “JobDetailFactoryBean“으로 변경 합니다.
  • JobDetailFactoryBean” 에서 “durability=”true“” 속성을 반드시 추가 해야 합니다.
    durability“은 한번만 사용을 할것인지 계속 사용할 것인지 옵션 입니다.
  • CronTriggerBean” 클래스 대신 “CronTriggerFactoryBean” 변경 합니다.

만약  위와 같이 설정을 하시지 않으면 “무수한 Error”를 경험 하실
겁니다.🙂

#Conclusion

지금까지” Spring + Quartz 설정”에 대해서 간략하게 설명 드렸습니다.
사실 “Spring + Quartz 연동“에 관련되서 많은 예제 와 설명들이
있습니다.

하지만 “Spring 3.x 이상 + Quartz 2.x“에 대한 예제는 많지 않습니다.
그래서 추후 도입시 많은 도움이 되었으면 합니다.

마지막으로 Jenkins 와 Quartz 도입에 대해서도 참고 하셔서
견고하고 관리가 용이한 Batch 시스템을 구축하시기 바랍니다.

예제 소스는 제 github에서 다운로드 받으세요🙂

https://github.com/beyondj2ee/s4q2

답글 남기기

아래 항목을 채우거나 오른쪽 아이콘 중 하나를 클릭하여 로그 인 하세요:

WordPress.com 로고

WordPress.com의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

Twitter 사진

Twitter의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

Facebook 사진

Facebook의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

Google+ photo

Google+의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

%s에 연결하는 중