Springboot

[Spring] Thread Pool과 Spring Scheduler

아몬드맛빼빼로 2024. 12. 30. 20:58
반응형

 

Scheduler의 문제점..?

@Component
class TestScheduler {

    @Scheduled(fixedDelay = 1000)
    @Throws(InterruptedException::class)
    fun task1() {
        Thread.sleep(10000)
        println("Task 1: ${LocalDateTime.now()}")
    }

    @Scheduled(fixedDelay = 1000)
    fun task2() {
        println("Task 2: ${LocalDateTime.now()}")
    }
}

이 코드를 실행하면 task1()가 1번 실행될 때 task2()를 10번 실행하여야 할 것 같지만 실제로는 

task1()이 종료되어야 task2()가 실행되는 것을 볼 수 있다. 즉, 스케쥴러로 2개 이상의 작업을 처리할 때 동시에 일어나지 않을 수 있다는 것이다. 이로 인하여 작업이 오래 걸리는 경우 예약된 다른 작업들이 지연될 수도 있다. 원인은 아주 단순한데 바로 스케쥴러는 단일 쓰레드를 기반으로 동작하기 때문이다. 이는 스레드를 늘리는 방식으로 해결할 수 있다.

Thread Pool

여러 개의 스레드를 유지하고 관리하기 위해 사용된다. 스레드는 한번 생성시키고 소멸하면 굉장히 많은 리소스를 소모하게 되기 때문에 매번 스레드를 생성시키고 없애기보다는, Thread Pool에서는 설정된 크기의 스레드를 만들어 놓고 해당 스레드들을 계속해서 재사용할 수 있도록 관리를 해준다.

Thread Pool의 문제

스레드를 불필요하게 많이 만들면 메모리 낭비가 심해질 것이다. 반대로 너무 적은 수를 생성하면 효율성이 떨어질 것이다. 이를 조절하기 위해서 어떤 작업이 많은지와 CPU의 가용 코어 수등을 고려하여 스레드의 수를 결정하는 것이 좋다.

Spring에서 구현

SchedulingConfigurer의 configureTasks를 재정의하여 스레드 사이즈를 조절해 주고 실행을 시켜보면

@Configuration
class SchedulerConfig : SchedulingConfigurer {
    override fun configureTasks(taskRegistrar: ScheduledTaskRegistrar) {
        val threadPool = ThreadPoolTaskScheduler()
        val n = Runtime.getRuntime().availableProcessors()
        threadPool.poolSize = n
        threadPool.initialize()
        taskRegistrar.setTaskScheduler(threadPool)
    }
}
더보기

Java Config를 이용하지 않고 application.yml에서 설정하는 방법도 있다

spring:
	task:
    	scheduling:
        	pool:
            	size: 10

Task2()가 1초에 한번씩 정상적으로 실행되고 Task1()은 10초에 한번씩 실행된다. 이는 여러 스레드로 스케쥴러가 동작하는 것임을 알 수 있다.