๊ด€๋ฆฌ ๋ฉ”๋‰ด

JAVAIARY

๋น„๋™๊ธฐ ๋ฉ€ํ‹ฐ ์Šค๋ ˆ๋“œ๋ฅผ ์œ„ํ•œ ThreadPoolTaskExecutor ๋ณธ๋ฌธ

lectureNote/JAVA

๋น„๋™๊ธฐ ๋ฉ€ํ‹ฐ ์Šค๋ ˆ๋“œ๋ฅผ ์œ„ํ•œ ThreadPoolTaskExecutor

shiherlis 2024. 1. 7. 15:24

๐Ÿ”—1. ๋น„๋™๊ธฐ ์ ์šฉํ•˜๊ธฐ

๐Ÿ”— 2. ๋น„๋™๊ธฐ ์ปค์Šคํ…€ 


1. ๋น„๋™๊ธฐ ์ ์šฉํ•˜๊ธฐ

SpringBoot ํ”„๋กœ์ ํŠธ์—์„œ๋Š” @EnableAsync ์–ด๋…ธํ…Œ์ด์…˜์„ ์ด์šฉํ•ด์„œ ๊ฐ„๋‹จํ•˜๊ฒŒ ๋น„๋™๊ธฐ๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค. 

๋น„๋™๊ธฐ๋ฅผ ์ ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์—๋Š” 2๊ฐ€์ง€๊ฐ€ ์žˆ๋‹ค.

1. SpringBootApplication ์— @EnableAsync ์–ด๋…ธํ…Œ์ด์…˜ ์‚ฌ์šฉํ•˜๊ธฐ
2. AsyncConfig ์ž‘์„ฑ (+@EnableAsync)

์ถ”๊ฐ€๋กœ ์›ํ•˜๋Š” ๋ฉ”์„œ๋“œ์— @Async ์–ด๋…ธํ…Œ์ด์…˜์„ ๋‹ฌ์•„์ฃผ๋ฉด ํ•ด๋‹น ๋ฉ”์†Œ๋“œ๊ฐ€ ๋น„๋™๊ธฐ๋กœ ์ž‘๋™ํ•˜๊ฒŒ ๋œ๋‹ค.

1) SpringBootApplication์— @EnableAsync 

@EnableAsync
@SpringBootApplication
public class MyApplication {

์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์— @EnableAsync ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ ๋งŒ์œผ๋กœ ๊ฐ„๋‹จํ•˜๊ฒŒ ๋น„๋™๊ธฐ๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.
ํ•˜์ง€๋งŒ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์— ์ง์ ‘ ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ, EnableAsync์˜ ๊ธฐ๋ณธ ์„ค์ •์œผ๋กœ ๋™์ž‘์„ ํ•˜๊ฒŒ ๋œ๋‹ค.

๏ผฅ๏ฝŽ๏ฝ๏ฝ‚๏ฝŒ๏ฝ…๏ผก๏ฝ“๏ฝ™๏ฝŽ๏ฝƒ๏ผŽ๏ฝŠ๏ฝ๏ฝ–๏ฝ

taskExecutor๋‚˜ Executor bean์„ ์ฐพ๋Š”๋ฐ ์—†๋‹ค๋ฉด SimpleAsyncTaskExecutor๋กœ ๋™์ž‘ํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. 
๊ทธ๋ž˜์„œ ๋Œ€๊ฐœ๋Š” TaskExecutor ๋ฅผ ๋”ฐ๋กœ ๊ตฌํ˜„ํ•ด์„œ ์‚ฌ์šฉํ•˜๊ฒŒ ๋œ๋‹ค.

2) AsyncConfig ํŒŒ์ผ ์ž‘์„ฑ

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;

@EnableAsync
@Configuration
public class AsyncConfig {
    private final int CORE_POOL_SIZE=3;
    private final int MAX_POOL_SIZE = 10;
    private int QUEUE_CAPACITY = 100_000;

    @Bean(name = "task-executor")
    public Executor threadPoolExecutor(){

        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();

        taskExecutor.setCorePoolSize(CORE_POOL_SIZE);   // core thread pool size
        taskExecutor.setMaxPoolSize(MAX_POOL_SIZE);     // ์š”์ฒญ queue size ์ด์ƒ์ผ ์‹œ thread Pool size
        taskExecutor.setQueueCapacity(QUEUE_CAPACITY);  // core full์ผ ์‹œ ๋Œ€๊ธฐ queue size
        taskExecutor.setThreadNamePrefix("myExecutor");

        return taskExecutor;
    }


}

Configuration ์—์„œ ์‚ฌ์šฉํ•  TaskExecutor์˜ property๋ฅผ ์„ค์ •ํ•ด ์ค„ ์ˆ˜ ์žˆ๋‹ค.
์ œํ•œ๋œ ThreadPool ์„ ์‚ฌ์šฉํ•  ๊ฒƒ์ด๋ฏ€๋กœ ThreadPoolTaskExecutor๋ฅผ ์‚ฌ์šฉํ•˜์˜€๋‹ค. 

  • CorePoolSize : 1. ๊ธฐ๋ณธ์ ์œผ๋กœ ์‚ฌ์šฉ๋  ์Šค๋ ˆ๋“œ ํ’€ ์‚ฌ์ด์ฆˆ
  • MaxPoolSIze :  3. queue๋„ ์ „๋ถ€ ์‚ฌ์šฉ์ค‘์ผ ๋•Œ ์ถ”๊ฐ€์š”์ฒญ์ด ์˜ฌ ์‹œ ์‚ฌ์šฉ๋˜๋Š” ์Šค๋ ˆ๋“œ ํ’€ ์‚ฌ์ด์ฆˆ
  • QueueCapacity : 2. ์ฝ”์–ด ์Šค๋ ˆ๋“œ๊ฐ€ ์ „๋ถ€ ์‚ฌ์šฉ์ค‘์ผ ๋•Œ ์ž‘์—…(task)์„ ๋Œ€๊ธฐ์‹œ์ผœ๋‘๋Š” queue์˜ ์‚ฌ์ด์ฆˆ

๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•ด ์ฃผ๋ฉด ์„ค์ •ํ•ด๋‘” ThreadNamePrefix์˜ ์ด๋ฆ„์˜ ์Šค๋ ˆ๋“œ๊ฐ€ ๋น„๋™๊ธฐ๋กœ ๋™์ž‘ํ•˜๊ฒŒ ๋œ๋‹ค.


 


2. ๋น„๋™๊ธฐ ์ปค์Šคํ…€

1์—์„œ ์„ค์ •ํ•ด ์ค€ AsyncConfig ์ž‘์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ ๋น„๋™๊ธฐ๋ฅผ ๊ตฌํ˜„ํ•ด ๋†“๊ณ  ํ…Œ์ŠคํŠธ๋ฅผ ํ•˜๋˜ ์™€์ค‘ DB๊ฐ’์„ ๋ชป ๋ฐ›์•„์˜ค๋Š” ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ๋‹ค. DB Schema ๋ฅผ ThreadLocal ์— ์ €์žฅํ•ด ๋‘์—ˆ๋Š”๋ฐ ๋น„๋™๊ธฐ ์Šค๋ ˆ๋“œ๋กœ ์˜ฎ๊ฒจ์„œ ์‹คํ–‰ํ•˜๊ฒŒ ๋˜๋ฉด์„œ ๊ฐ’์„ ๋ถˆ๋Ÿฌ์˜ค์ง€ ๋ชปํ•˜๊ฒŒ ๋œ ๊ฒƒ. ThreadLocal ์ฐธ์กฐ

๊ทธ๋ž˜์„œ TaskDecorator ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ TaskDecoratorImpl ํด๋ž˜์Šค๋กœ ๊ตฌํ˜„ํ•ด์ฃผ์–ด ์ด์ „ ์Šค๋ ˆ๋“œ์— ์žˆ๋˜ DB์ •๋ณด๋ฅผ ์ƒˆ ์ž‘์—…์Šค๋ ˆ๋“œ์— ๋ณต์‚ฌํ•  ์ˆ˜ ์žˆ๋„๋ก ํ–ˆ๋‹ค. 

import lombok.extern.slf4j.Slf4j;
import org.springframework.core.task.TaskDecorator;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class TaskDecoratorImpl implements TaskDecorator {
    @Override
    public Runnable decorate(Runnable runnable) {   //ThreadLocal ์ •๋ณด ๋น„๋™๊ธฐ์Šค๋ ˆ๋“œ์— ๋ณต์‚ฌ
        String schema = DBConfig.getDBSchema();
        return ()-> {
            DBConfig.addDBSchema(schema);
            log.info("AsyncThread - DBConfig: "+DBConfig.getDBSchema());
            runnable.run();
        };
    }
}