spring scheduler 작동 원리

lsy·2022년 12월 12일
2

필요한 것

spring sheduler를 사용하기 위해서 다음 작업이 필요하다.

  1. @EnableScheduling 등록
  2. @Scheduledcron 옵션을 통해 작업 등록

1초마다 "test..."를 출력하는 메서드를 등록했다.

작동 원리 파악

현재 위치: @EnableScheduling

@EnableScheduling 어노테이션에서 시작한다. 어노테이션 내부를 보자.

@Import(SchedulingConfiguration.class)가 보인다.

@Import 어노테이션은 @Configuration으로 설정한 클래스를 두 개 이상 사용하는 경우 spring에게 해당 클래스를 인식시키기 위해 사용한다.

그렇다면 SchedulingConfiguration 클래스에는 무언가 Bean으로 등록하고 있다는 소리다. 살펴보자.

현재 위치: SchedulingConfiguration 클래스

ScheduledAnnotationBeanPostProcessor 클래스를 Bean으로 등록하고 있다. 그렇다면 해당 클래스는 무슨 일을 하는 것일까? 더 들어가보자.

현재 위치: ScheduledAnnotationBeanPostProcessor 클래스

우선 생성자에서 registrar 멤버에 ScheduledTaskRegistrar 인스턴스를 생성하고 있다. 이건 나중에 한 번 살펴보자.

밑에 코드를 보면Bean 초기화 전/후처리 메서드가 존재한다.

후처리 메서드인 postProcessAfterInitialization() 메서드를 한 번 살펴보자.

검정색 박스 부분에서 @Scheduled가 달려 있는 메서드를 스캔해서 annotatedMethods map에 저장한다.

후에 빨간색 박스 부분에서 스캔한 메서드들을 이용해 processScheduled()을 호출한다.

processScheduled()에서 맨 처음에 입력한 cron식이나, fixedDelay에 대해 제대로 입력했는지 유효성을 검사한다.

이후에 cron식을 사용했다면, registrar.scheduleCronTask()를 실행해 CronTask 인스턴스와 CronTrigger인스턴스를 인자로 넣어주게 된다.

정확히는 CronTask 생성자에 CronTrigger가 등록되고, CronTaskregistrar.scheduleCronTask()의 인자로 넘어간다.

registrar는 아까 우리가 맨 처음에 생성자에서 봤던 바로 그 registrar 멤버다.

따라서 ScheduledTaskRegistrar.scheduleCronTask()를 실행하면서 CronTask 인스턴스를 인자로 넘겨주게 된다. 한 번 따라가보자.

현재 위치: ScheduledTaskRegistrar 클래스

ScheduledTask 인스턴스를 생성해 Task에 대한 정보를 등록해서 return하고 있다.

검정색 박스 부분을 보면, ScheduledTask.future 부분에 taskScheduler.schedule()을 통해CronTask를 넘겨주고 있다. taskScheduler로 한 번 넘어가보자.

현재 위치: TaskScheduler 인터페이스

TaskScheduler는 인터페이스였다. 설명을 읽어보면 ThreadPoolTaskScheduler 클래스가 이 인터페이스의 Default 구현체라고 한다.

그렇다면 taskScheduler.schedule()에서 taskScheduler는 바로 ThreadPoolTaskScheduler일 것이다. 해당 클래스의 schedule() 메서드로 가보자.

현재 위치: ThreadPoolTaskScheduler 클래스

ScheduledExecutorService 인터페이스를 생성하고 있다. ScheduledExecutorService 인터페이스에 쓰레드 풀을 이용해 우리가 등록한 작업을 실행시켜줄 구현체가 들어갈 것이다.

이를 new ReschedulingRunnable()에 넘겨주고 있다. 그리고 schedule() 메서드를 실행한다. ReschedulingRunnable 클래스로 가보자.

현재 위치: ReschedulingRunnable 클래스

ReschedulingRunnable.schedule() 메서드에서 delay를 계산 후, 해당 시간이 지나면 시작하는 작업을 excutor.schedule()을 통해 등록한다. executor는 위에서 언급한 ScheduledExecutorService의 구현체다.

이후에 executor는 delay 후 쓰레드를 할당해 run() 메서드를 실행하게 된다. 빨간 박스를 보면 실행이 다 끝난다면 다시 ReschedulingRunnable.schedule() 메서드를 호출해 작업을 executor에 재등록한다.

이렇게 우리가 지정한 시간대로 작업이 무한히 실행되는 것이다.

결론

  1. spring server 시작시 ScheduledAnnotationBeanPostProcessor 클래스가 Bean으로 등록됨.
  2. Bean 등록 후 후처리 메서드postProcessAfterInitialization()가 실행. 이 때 @Scheduled가 달려있는 메서드 스캔 후 Map에 저장.
  3. processScheduled()메서드 호출하여 해당 Map을 넘겨줌.
  4. processScheduled()에서 cron, fixedDelay 중 어떤 옵션을 선택했느냐에 따라 각각 알맞은 Task 진행.
  5. cron으로 진행했다고 가정. processScheduled()에서 ScheduledTaskRegistrar.scheduleCronTask() 호출. 이 때 Task와 Delay에 대한 정보를 넘겨줌.
  6. 이후 여러 클래스들을 거쳐 ScheduledExecutorService의 구현체(이하 excutor)에 Schedule() 메서드에 Task와 Delay 등록됨.
  7. Delay가 지나면 excutor가 쓰레드풀에서 쓰레드를 할당하여 run()메서드 실행하여 Task 실행.
  8. run()메서드에는 excutor에 다시 Task와 Delay를 설정하도록 되어있음. 따라서 다시 해당 Delay 후에 Task 실행.
  9. 무한 반복.

이렇게 진행되겠다.

즉 결국엔 Spring Scheduler는 특별한 것을 이용하고 있는 것이 아니라 자바의 java.util.concurrent패키지에 있는 Executor를 사용하고 있던 것이다.

Reference

https://pompitzz.github.io/blog/Spring/Scheduler.html#_3-taskscheduler%E1%84%8B%E1%85%A6%E1%84%89%E1%85%A5-%E1%84%8C%E1%85%A1%E1%86%A8%E1%84%8B%E1%85%A5%E1%86%B8%E1%84%8B%E1%85%B3%E1%86%AF-%E1%84%89%E1%85%B3%E1%84%8F%E1%85%A6%E1%84%8C%E1%85%AE%E1%86%AF%E1%84%85%E1%85%B5%E1%86%BC

profile
server를 공부하고 있습니다.

0개의 댓글