SpringのTaskSchedulerのうち、もっともよく使われるorg.springframework.scheduling.concurrent.ThreadPoolTaskExecutorの設定ミスが多いです。
corePoolSizemaxPoolSizequeueCapacity
が設定項目としてあります。
これだけ見ると、このExecutorがTheadを作る順としては
corePoolSizeをThreadを最初に作るcorePoolSizeが一杯になるとmaxPoolSizeまでThreadを増やすmaxPoolSizeを越えると、queueCapacityまでキューイングするqueueCapacityを越えるとrejectされる
と思いがちです。
しかし、これは誤解です。
正しくは
corePoolSizeまでThreadを作るcorePoolSizeが一杯になるとqueueCapacityまでキューイングするqueueCapacityを越えるとmaxPoolSizeまでThreadを増やすmaxPoolSizeを越えるとrejectされる
です。
corePoolSizeのデフォルト値は1、maxPoolSize,queueCapacity共にデフォルト値はInteger.MAX_VALUEです。
次のような設定は意味がありません。
@Bean
ThreadPoolTaskExecutor threadPoolTaskExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(4);
taskExecutor.setMaxPoolSize(40);
return taskExecutor;
}
queueCapacityを設定していないので、最大スレッド数は4です。それ以上のリクエストは全てキューイングされます。40という数字に意味がありません。
@Bean
ThreadPoolTaskExecutor threadPoolTaskExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setMaxPoolSize(40);
return taskExecutor;
}
corePoolSizeもqueueCapacityを設定していないので、最大スレッド数は1です。それ以上のリクエストは全てキューイングされます。
スレッド数を4-40に設定したいのであれば、例えば次のように設定する必要があります。(ただし、初回に4スレッドできる訳ではありません)
@Bean
ThreadPoolTaskExecutor threadPoolTaskExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(4);
taskExecutor.setQueueCapacity(4);
taskExecutor.setMaxPoolSize(40);
return taskExecutor;
}
設定値を次の順番で認識しておいたほうが間違いにくいです。
corePoolSizequeueCapacitymaxPoolSize
あるいは最大スレッド数が40でることのみ指定したい場合は
@Bean
ThreadPoolTaskExecutor threadPoolTaskExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(40);
return taskExecutor;
}
でOKです。
ちなみにThreadPoolTaskExecutorが委譲しているjava.util.concurrent.ThreadPoolExecutorを作るときは次のような使い方をすることが多いと思います。
ExecutorService executorService = Executors.newFixedThreadPool(40);
これは
@Bean
ThreadPoolTaskExecutor threadPoolTaskExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(40);
taskExecutor.setMaxPoolSize(40);
return taskExecutor;
}
に近いです。
Spring Bootを使う場合は、上記のようにThreadPoolTaskExecutorを定義するよりもAutoConfigurationを使用すべきです。
https://docs.spring.io/spring-boot/reference/features/task-execution-and-scheduling.html#page-title
この場合は、次のプロパティを使ってPool sizeを設定できます。
spring.task.execution.pool.core-size=4
spring.task.execution.pool.queue-capacity=4
spring.task.execution.pool.max-size=40
直感的にThreadを増やすことを先に行なって欲しい場合は以下のriptide-concurrentの使用を検討してみてください。
https://github.com/zalando/riptide/tree/main/riptide-concurrent