2.Spring Scheduling
본 장에서는 TaskExecutor와 TaskScheduler에 대해 간단히 살펴볼 것이다. 또한 XML과 Annotation 기반에서 이들을 사용하는 방법에 대해 알아보도록 하자.
TaskExecutor는 java.util.concurrent.Executor를 extends하여 정의된 인터페이스로써 지정된 Task를 실행시키기 위한 execute(Runnable task) 메소드를 정의하고 있다. Spring에서는 다양한 TaskExecutor 구현체를 제공하고 있으며 각 구현체에 대해서는 Spring Documentation 내의 TaskExecutor types를 참조하도록 한다.
Spring에서 제공하는 TaskExecutor를 사용하여 특정 Task를 실행시키는 TaskExecutor를 개발하기 위해서는 XML 기반으로 Spring TaskExecutor의 속성을 정의한 후, 구현 대상이 되는 TaskExecutor에서 정의된 Spring TaskExecutor를 Inject하여 사용하면 된다. 해당 TaskExecutor 내에서는 Inject한 Spring TaskExecutor의 execute() 메소드를 호출함으로써 Task를 실행할 수 있으며 Task Execution을 위한 Rule은 자체적으로 구현해야 한다. 한편 Thread 형태로 구현된 Task는 Spring TaskExecutor 구현체의 특성에 맞게 Thread Pool에 관리될 것이다.
public class PrintTaskExecutor {
private TaskExecutor executor;
public PrintTaskExecutor(TaskExecutor taskExecutor) {
this.executor = taskExecutor;
}
public void print() {
for (int i = 0; i < 3; i++) {
executor.execute(new Task(i));
}
}
private class Task implements Runnable {
private int no;
public Task(int no) {
this.no = no;
}
public void run() {
System.out.println("execute a Task" + no + " at " + new Date()
+ " with TaskExecutor");
}
}
}
위 코드는 print() 메소드 내에서 Spring TaskExecutor를 활용하여 Inner 클래스로 정의된 Task를 실행하는 PrintTaskExecutor의 일부이다. PringTaskExecutor의 print() 메소드를 호출하면 Thread 유형의 내부 Task에 구현된 run() 메소드가 3회 실행되는 것을 확인할 수 있을 것이다.
다음은 위에서 언급한 PrintTaskExecutor에 대한 속성 정의 내용의 일부이다.
<task:executor id="executor" pool-size="4" queue-capacity="4" rejection-policy="ABORT"/>
<bean id="task" class="anyframe.sample.scheduling.task.executor.PrintTaskExecutor">
<constructor-arg ref="executor"/>
</bean>
위에서 언급한 PrintTaskExecutor 샘플 코드는 본 섹션 내의 다운로드 - anyframe.sample.scheduling을 통해 다운로드받을 수 있다.
다음은 Spring 3에서 새롭게 제공하고 있는 org.springframework.scheduling.TaskScheduler 클래스의 일부 내용이다.
public interface TaskScheduler {
ScheduledFuture schedule(Runnable task, Trigger trigger);
ScheduledFuture schedule(Runnable task, Date startTime);
ScheduledFuture scheduleAtFixedRate(Runnable task, Date startTime, long period);
ScheduledFuture scheduleAtFixedRate(Runnable task, long period);
ScheduledFuture scheduleWithFixedDelay(Runnable task, Date startTime, long delay);
ScheduledFuture scheduleWithFixedDelay(Runnable task, long delay);
}
TaskScheduler는 Execution 대상이 되는 Task를 특정 시점 이후에 한 번 실행하거나 fixedRate 또는 fixedDelay 정보를 기반으로 주기적으로 실행할 수 있는 메소드를 제공하고 있다.
Spring 3에서는 앞서 언급한 TaskExecutor(<task:executor/>)나 TaskScheduler(<task:scheduler/>)에 대한 속성 정의를 위해 task라는 Namespace를 제공한다. 또한 이를 이용하면 간편하게 Task Scheduling(<task:scheduled-task/>)을 위한 속성을 정의할 수 있게 된다. task Namespace를 사용하기 위해서는 해당 XML 파일 내의 <beans> 정의시 spring-task.xsd를 선언해 주어야 한다.
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:task="http://www.springframework.org/schema/task"
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.xsd">
...
</beans>
다음은 <task:scheduler/>를 사용한 속성 정의의 일부이다. 다음과 같이 속성을 정의한 경우 정의된 Pool Size를 기반으로 ThreadPoolTaskScheduler 인스턴스가 생성될 것이다. 정의된 id는 Pool에 관리될 Task Thread의 Prefix로 사용된다.
<task:scheduler id="scheduler" pool-size="10"/>
다음은 <task:executor/>를 사용한 속성 정의의 일부이다. 다음과 같이 속성을 정의한 경우 ThreadPoolTaskExecutor 인스턴스가 생성될 것이다. 또한 정의된 id는 Pool에 관리될 Task Thread의 Prefix로 사용된다.
<task:executor id="executor" pool-size="4" queue-capacity="4" rejection-policy="ABORT"/>
<task:executor/>는 <task:scheduler/>에 비해 다양한 속성 정의를 지원한다. 다음에서는 정의 가능한 속성들에 대해 자세히 살펴보도록 하자.
다음은 task Namespace의 가장 강력한 특징인 <task:scheduled-task/>를 사용한 속성 정의의 일부이다. <task:scheduled-task/>는 기본적으로 'scheduler'라는 속성을 가지고 있는데 이것은 내부에 정의된 Task를 Scheduling하기 위한 TaskScheduler Bean을 정의하기 위한 것이다. <task:scheduled-task/>는 하위에 다수의 <task:scheduled/>를 포함할 수 있으며 <task:scheduled/>의 'ref'와 'method'는 실행 대상이 되는 Bean과 해당 Bean 내에 포함된 실행 대상 메소드를 정의하기 위한 속성이다.
<task:scheduled-tasks scheduler="scheduler">
<task:scheduled ref="task" method="printWithFixedDelay" fixed-delay="5000"/>
<task:scheduled ref="task" method="printWithFixedRate" fixed-rate="10000"/>
<task:scheduled ref="task" method="printWithCron" cron="*/8 * * * * MON-FRI"/>
</task:scheduled-tasks>
<task:scheduler id="scheduler" pool-size="10"/>
<task:scheduled/>는 'ref', 'method' 외에 Scheduling을 위해 필요한 속성을 가지는데 각각에 대해 알아보면 다음과 같다.
위에서 언급한 PrintTaskExecutor 샘플 코드는 본 섹션 내의 다운로드 - anyframe.sample.scheduling을 통해 다운로드받을 수 있다.
2.4.Annotation based Scheduling & Asynchronous Execution
Spring 3에서는 Task Scheduling(@Scheduled)과 Aynchronous Task Execution(@Async)을 위한 Annotation을 제공한다. 이 Annotation들을 인식할 수 있도록 하기 위해서는 다음과 같은 속성 정의가 추가되어야 한다.
<task:annotation-driven scheduler="scheduler" executor="executor"/>
다음에서는 @Scheduled와 @Async Annotation에 대해 살펴보기로 하자.
@Scheduled는 메소드 단위로 사용 가능하며 실행 주기 정의를 위한 'fixedDelay', 'fixedRate', 'cron'과 같은 속성들을 제공하고 있다. 각 속성의 의미는 XML based Scheduling에서 언급한 <task:scheduled/> 속성과 동일한 의미를 가진다. @Scheduled 메소드에 대한 실행은 TaskScheduler가 담당한다.
@Scheduled(fixedDelay=5000)
public void printWithFixedDelay() {
System.out.println("execute printWithFixedDelay() of Annotated PrintTask at "
+ new Date());
}
@Scheduled(fixedRate=10000)
public void printWithFixedRate() {
System.out.println("execute printWithFixedRate() of Annotated PrintTask at "
+ new Date());
}
@Scheduled(cron="*/8 * * * * MON-FRI")
public void printWithCron() {
System.out.println("execute printWithCron() of Annotated PrintTask at "
+ new Date());
}
@Scheduled Annotation을 부여한 메소드는 입력 인자를 갖지 않고, Return 값이 없어야 함에 유의하도록 한다. 또한 메소드 로직 실행을 위해 다른 Bean을 참조로 해야 한다면 Dependency Injection에 의해 처리하도록 한다.
2.4.2.Asynchronous Execution
@Async는 메소드 단위로 사용 가능하며 비동기적으로 특정 메소드를 실행하고자 할 때 사용할 수 있다. @Async 메소드에 대한 실제적인 실행은 TaskExecutor에 의해 처리된다.
@Async
public void printWithAsync() throws Exception {
System.out.println("execute printWithAsync() of AsyncPrintTask at "
+ new Date());
Thread.sleep(5000);
}
@Async
public void printWithArg(int i) throws Exception {
System.out.println("execute printWithArg(" + i + ") of AsyncPrintTask at "
+ new Date());
Thread.sleep(5000);
}
@Async
public Future<String> returnVal(int i) throws Exception {
System.out.println("execute returnVal() of AsyncPrintTask");
Date current = new Date();
Thread.sleep(5000);
return new AsyncResult<String>(Integer.toString(i));
}
위 코드에서와 같이 @Async 메소드는 @Scheduled 메소드와 다르게 입력 인자나 리턴값을 가질 수 있다. @Scheduled의 경우에는 Spring Container에 의해 관리되는 반면에 @Async는 Caller에 의해 직접 호출되기 때문이다. 단, 리턴값의 경우 Future 타입 형태만 가능하며 Caller는 비동기적으로 실행 종료된 메소드의 결과를 Future 객체의 get() 메소드를 통해 알아낼 수 있다.
위에서 언급한 PrintTaskExecutor 샘플 코드는 본 섹션 내의 다운로드 - anyframe.sample.scheduling을 통해 다운로드받을 수 있다.
출처 - http://dev.anyframejava.org/docs/anyframe/plugin/scheduling/4.5.2/reference/html/ch02.html
A Cron Expressions
Cron expressions are used to configure instances of CronTrigger, a subclass of org.quartz.Trigger. A cron expression is a string consisting of six or seven subexpressions (fields) that describe individual details of the schedule.
These fields, separated by white space, can contain any of the allowed values with various combinations of the allowed characters for that field. Table A-1 shows the fields in the expected order.
Example A-1 Cron Expressions
Cron expressions can be as simple as * * * * ? *
or as complex as 0 0/5 14,18,3-39,52 ? JAN,MAR,SEP MON-FRI 2002-2010
.
Here are some more examples:
출처 - http://javahunter.wordpress.com/2011/05/05/cronscheduler-in-spring/