Spring 예약 작업처리

뾰족머리삼돌이·2025년 1월 9일
0

Spring

목록 보기
12/14

Spring Framework의 Task Execution and Scheduling 을 부분번역한 포스팅입니다.

TaskScheduler

public interface TaskScheduler {

	Clock getClock();

	ScheduledFuture schedule(Runnable task, Trigger trigger);

	ScheduledFuture schedule(Runnable task, Instant startTime);

	ScheduledFuture scheduleAtFixedRate(Runnable task, Instant startTime, Duration period);

	ScheduledFuture scheduleAtFixedRate(Runnable task, Duration period);

	ScheduledFuture scheduleWithFixedDelay(Runnable task, Instant startTime, Duration delay);

	ScheduledFuture scheduleWithFixedDelay(Runnable task, Duration delay);

Spinrg에서는 특정시점 이후에 작업이 실행되게 스케줄링 할 수 있도록 TaskScheduler 서비스 추상화 인터페이스를 제공한다.

해당 인터페이스에서 가장 간단한 메서드는 RunnableInstant를 받는 schedule()다. 이는 지정된 시간이후에 한번 작업이 실행되도록 하며, 나머지 메서드들은 작업의 반복실행 기능을 제공한다.

fixed-ratefixed-delay는 주기적인 작업실행에 적합하며, Trigger를 받는 schedule()은 좀 더 유연한 실행을 지원한다.

이렇게 하나의 SPI를 제공함으로써 스케줄링 로직과 배포환경 간의 의존성을 제거하는 효과가 있다. 각 환경에 걸맞는 구현체를 제공하기 때문에 상황에 맞춰 구현체를 변경할 수 있기 때문이다.

예를들어, 로컬환경에서는 ThreadPoolTaskSchedulerConcurrentTaskScheduler같은 간단한 구현체를 사용할 수 있다. 배포환경에서는 서버가 스레드 풀을 관리하도록 DefaultManagedTaskScheduler로 변경해주면 된다.

하나의 최상위 인터페이스를 구현하고있기 때문에 애플리케이션 코드를 변경하지않고 손쉽게 구현체를 변경할 수 있게된다.

Trigger

public interface Trigger {

	Instant nextExecution(TriggerContext triggerContext);
}

Trigger 인터페이스는 하나의 추상메서드를 가지는 단순한 구조를 지닌다. 기본적인 아이디어는 과거의 실행결과 혹은 임의의 조건에 따라 Trigger가 실행되는 시간이 결정되는 것이다. 이와 관련하여 자세히 살펴봐야할 부분은 매개변수로 받는 TriggerContext 인터페이스다.

public interface TriggerContext {

	Clock getClock();

	Instant lastScheduledExecution();

	Instant lastActualExecution();

	Instant lastCompletion();
}

TriggerContext는 관련된 데이터들을 캡슐화하며, 향후 확장을 위해 개방되어있다. 기본적으로 SimpleTriggerContext라는 구현체가 사용된다.

추상메서드들의 이름에서 알 수 있듯이, 작업이 마지막 실제 실행시간, 마지막 예약 실행시간, 마지막 완료시간 등을 관리한다.

Trigger 구현체

Spring에서는 CronTriggerPeriodicTrigger라는 2개의 Trigger 구현체를 제공한다.

CronTrigger

CronTrigger는 Cron 표현식 기반으로 스케줄링을 진행한다. 예를들어, 평일 9시부터 5시까지의 업무시간동안 매 시 15분마다 실행될 작업은 아래와 같이 표현할 수 있다.

scheduler.schedule(task, new CronTrigger("0 15 9-17 * * MON-FRI"));

여기에 사용되는 Cron 표현식은 0 15 9-17 * * MON-FRI 부분이며, 아래 구조도로 해석할 수 있다.

 ┌───────────── second (0-59)
 │ ┌───────────── minute (0 - 59)
 │ │ ┌───────────── hour (0 - 23)
 │ │ │ ┌───────────── day of the month (1 - 31)
 │ │ │ │ ┌───────────── month (1 - 12) (or JAN-DEC)
 │ │ │ │ │ ┌───────────── day of the week (0 - 7)
 │ │ │ │ │ │          (0 or 7 is Sunday, or MON-SUN)
 │ │ │ │ │ │
 * * * * * *

PeriodicTrigger

PeriodicTrigger는 고정시간, 선택적으로 초기 지연 값, 그리고 해당 기간을 fixed-rate로 해석할지 fixed-delay로 해석할지를 표현하는 boolean 값으로 구성된다. 이미 TaskSchedulerfixed-ratefixed-delay로 작업을 스케줄링하는 메서드를 가지고 있기 때문에 일반적으로는 해당하는 메서드들을 사용하는게 적합하다.

PeriodicTrigger의 가치는 트리거 추상화를 사용하는 컴포넌트에서 트리거들을 전환하며 사용할 수 있다는 데에 있다.
즉, 의존성 주입을 이용해 컴포넌트 외부에서 손쉽게 트리거를 설정하여 동작방식을 수정하거나 확장할 수 있다.

애노테이션을 이용한 스케줄링

앞서 소개한 TaskScheduler를 직접적으로 사용하지 않아도 @Scheduled 애노테이션을 활용하면 손쉽게 스케줄링 작업을 설정할 수 있다. 우선 사전작업으로 Configuration 파일에 @EnableScheduling 애노테이션으로 스케줄링을 활성화해줘야 한다.

애노테이션 지원 외에 세부적인 설정이 필요하다면 SchedulingConfigurer를 추가로 구현하면 된다.
애노테이션을 이용하는 경우, 기본적으로 사용되는 구현체는 ThreadPollTaskScheduler이며 설정을 통해 변경할 수 있다.

@Scheduled(fixedDelay = 5000)
public void doSomething() {
	// something that should run periodically
}

애노테이션의 사용방법은 원하는 작업 메서드위에 @Scheduled를 설정하는 것이다. 위 예시는 fixedDelay이므로, doSomething()이라는 메서드를 5초마다 실행한다. 즉, 이전 작업의 완료시간부터 5초 후 실행된다.

기본적으로 fixed-delay, fixed-rate를 포함한 딜레이 값의 단위는 모두 밀리세컨드 단위이다.
만약 다른 시간단위를 사용하고 싶다면 timeUnit 속성으로 원하는 시간단위를 설정해주면 된다.

@Scheduled(fixedDelay = 5, timeUnit = TimeUnit.SECONDS)
public void doSomething() {
	// something that should run periodically
}
@Scheduled(fixedRate = 5, timeUnit = TimeUnit.SECONDS)
public void doSomething() {
	// something that should run periodically
}

fixedRate로 설정한 경우에는 매 5초마다 실행된다. fixedDelay와 다르게 이전 작업의 완료시간과 독립적으로 5초가 지날때마다 작업이 실행된다. initialDelay 값을 설정한다면 첫 작업 실행이전에 지연시간을 설정하는 것도 가능하다.

단순한 주기적인 스케줄링이 부족하다고 판단된다면 Cron식을 이용하면 된다.
Cron식이 사용하기 번거롭다면 Spring에서 제공하는 매크로를 이용해도 된다.

@Scheduled(cron="*/5 * * * * MON-FRI")
public void doSomething() {
	// something that should run on weekdays only
}

주의사항으로 반복작업으로 예약할 메서드는 런타임시에 명시적으로 동작하는게 아니므로, 매개변수를 가져서는 안되며 void 반환형을 가져야한다. 만약, 다른 객체와 상호작용해야하는 작업이라면 클래스 단위에서 의존성 주입으로 받아와 사용해야한다.

또한, 한 메서드에 여러개 설정도 가능하다.
이 경우에는 각각의 애노테이션들이 독립적으로 동작하기때문에 각 일정들이 겹치지 않게 주의해야한다.

출처

0개의 댓글

관련 채용 정보