본문 바로가기
실습/리눅스 서버 + 스프링 부트

Scheduler

by 이민우 2022. 1. 15.
728x90
반응형

Scheduler

 

프로그래밍을 하다보면 주기적으로 실행되어야 하는 코드들이 있다.

스케줄러는 이러한 때에 사용되는 코드로, 특정 시간 혹은 특정 간격을 바탕으로 주기적으로 실행되는 코드이다.

 

 

 

스프링 부트에서 Scheduler를 시용하기 위해서는 먼저 SpringBootApplication에 @EnableScheduling 어노테이션을 추가한다.

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

@EnableScheduling //스케줄링 사용 명시
@SpringBootApplication
public class SchedulertestApplication {

	public static void main(String[] args) {
		SpringApplication.run(SchedulertestApplication.class, args);
	}

}

 

 

그 후에는 스케줄러를 클래스로 선언하여 클래스에는 @Component, 메소드에는 @Scheduled를 추가하여 사용하면 된다. 이 때 유의할 점은, 메소드는 void 타입으로 선언하고, 매개변수를 명시하지 말아야 한다.

 


 

스프링 스케줄러는 크게 세 가지 방법으로 사용할 수 있다.

가장 첫 번째는 리눅스의 crontab과 유사한 cron을 사용하는 것이다.

 

먼저 application.properties에 주기를 명시하자.

이 때 순서는 초-분-시-일-월-요일 순이다. (리눅스는 분-시-일-월-요일)

아래는 5초마다 한 번씩 실행함을 명시한 것이다.

schedule.cron=*/5 * * * * *

 

그 후에는 위에서 설명한 것과 마찬가지로 코드를 작성한다.

단순 공부용이므로 그냥 카운트와 현재 시간을 출력하는 코드로 작성하였다.

package com.example.demo;

import java.util.Date;

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class SchedulerBean {
	private static int count = 0;

	@Scheduled(cron = "${schedule.cron}")
	public void scheduleBean() {
		System.out.println(++count + " : " + (new Date()).toString());
	}
}

 

그리고 코드를 실행시키면 다음과 같은 결과가 나온다.

 

 


 

다음 방법은 fixedDelay를 사용하는 것이다.

fixedDelay는 이전 작업이 종료된 후 설정된 시간에 따라 이후 다시 시작된다.

 

그리고 코드는 위의 코드에서 @Scheduled 내부의 값만 변경한다.위에서와 마찬가지로 5초마다 실행되도록 5000을 명시하였다.

package com.example.demo;

import java.util.Date;

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class SchedulerBean {
	private static int count = 0;

	@Scheduled(fixedDelay = 5000)
	public void scheduleBean() {
		System.out.println(++count + " : " + (new Date()).toString());
	}
}

 

그리고 코드를 실행시키면 다음과 같은 결과물이 나온다.

 

 

그런데 여기서 명심해야 할 점은, fixedDelay는 정확히 "이전 작업이 종료된 후" 설정한 시간이 지나야 함수를 수행한다는 점이다.

 

위의 메소드는 정말 단순하게 한 줄을 출력하는 것이 전부인 메소드이기에, 당연히 실행 시간또한 길지 않아 사전 설정한 fixedDelay인 5초에 한 번씩 실행이 되었다.

하지만 그보다 오래 걸리는 작업이 존재한다면 어떻게 될까?

 

예를 들어 메소드 실행에 2초가 걸리는 작업이 있다고 가정하자, 그렇다면 fixedDelay를 5000으로 맞추어놓았다면 해당 함수는 5초마다가 아닌, 7초마다 실행이 되게 된다.

 

해당 내용이 맞는지 확인을 해보기 위해 아래와 같은 함수를 작성하였다.

package com.example.demo;

import java.util.Date;

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class SchedulerBean {
	private static int count = 0;

	@Scheduled(fixedDelay = 5000)
	public void scheduleBean() throws InterruptedException {
		System.out.println(++count + " : " + (new Date()).toString());
		Thread.sleep(2000);
	}
}

 

그리고 코드를 실행해보면 아래의 결과처럼 함수가 7초에 한 번씩 실행되는 것을 볼 수 있다.

 

 


그렇다면 실행 시간에 상관없이 정확히 5초에 한 번씩 함수를 실행시키고자 한다면 어떻게 해야할까?

그 해답은 마지막 세 번째 방법인 fixedRate을 사용하는 것이다.

 

package com.example.demo;

import java.util.Date;

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class SchedulerBean {
	private static int count = 0;

	@Scheduled(fixedRate = 5000)
	public void scheduleBean() throws InterruptedException {
		System.out.println(++count + " : " + (new Date()).toString());
		Thread.sleep(2000);
	}
}

 

fixedRate은 이전 메소드의 실행시간에 관계없이, 그저 설정 시간만을 간격으로 작업을 수행한다.

그래서 위의 메소드에 대한 결과는 아래와 같다.

 

그런데 여기서 또 주의해야 할 점이 있다.

메소드의 실행시간이 사전에 설정한 간격을 초과하는 경우이다.

 

위의 경우에는 메소드 실행 시간은 2초이고, 간격은 5초였기에 정확히 5초만에 한 번씩 실행이 되었었다.

하지만 메소드의 실행 시간이 간격으로 설정한 시간을 초과한다면 어떻게 될까?

Thread.sleep을 6000으로 맞춰놓고 실험을 해보면 아래와 같은 결과가 나온다.

 

정확히 5초가 아닌, 메소드의 실행 시간인 6초 간격으로 실행되고 있다.

 

만약 이를 방지하고 싶다면, 클래스에는 @EnableAsync를, 메소드에는 @Async 어노테이션을 붙이면 해결된다.

package com.example.demo;

import java.util.Date;

import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
@EnableAsync
public class SchedulerBean {
	private static int count = 0;

	@Async
	@Scheduled(fixedRate = 5000)
	public void scheduleBean() throws InterruptedException {
		System.out.println(++count + " : " + (new Date()).toString());
		Thread.sleep(6000);
	}
}

 

 

 

추가로 Scheduled 어노테이션에는 initialDelay라는 파라미터가 있다.

위의 함수들을 실행시켜 보면 알겠지만 fixedRate나 fixedDelay는 스프링 부트가 실행되자마자 실행된다.

만약 initialDelay를 5000으로 설정했을 경우, 위의 스케줄러들은 실행과 동시에 작동하지 않고, initialDelay 값인 5초 후에 작동된다.

 

728x90
반응형

'실습 > 리눅스 서버 + 스프링 부트' 카테고리의 다른 글

Filter, Interceptor, AOP  (0) 2022.02.01
ntp를 이용한 타임서버 구축  (0) 2022.01.22
JPA  (0) 2021.11.27
Spring Security  (0) 2021.11.07
HAProxy를 활용한 IFrame 활성화  (0) 2021.09.26