Async

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

How do code?

setting gradle, bean

gradle
runtimeOnly("com.h2database:h2")
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation("org.springframework.boot:spring-boot-starter-web")
@EnableAsync
@Configuration
class ExecutorConfig {

    @Bean
    fun executor() = ThreadPoolTaskExecutor()
        .also {
            it.corePoolSize = 2
            it.maxPoolSize = 2
            it.queueCapacity= 10
            it.setThreadNamePrefix("junnyland-")
            it.initialize()
        }
}

๋น„๋™๊ธฐ ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ๋‘ ๊ฐ€์ง€๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

์ฒซ ๋ฒˆ์งธ ๋ฐฉ๋ฒ•์€ ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๊ณ  ๋ฐ˜ํ™˜๊ฐ’์ด ์—†๋Š” ๊ฒฝ์šฐ. ๋‘ ๋ฒˆ์งธ ๋ฐฉ๋ฒ•์€ Future ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ Future ๊ฐ์ฒด๋Š” ๋น„๋™๊ธฐ ์ž‘์—…์˜ ๊ฒฐ๊ณผ๋ฅผ ๋‚˜ํƒ€๋‚ด๋ฉฐ, ์ž‘์—…์ด ์™„๋ฃŒ๋˜๊ธฐ ์ „์—๋Š” ๋ธ”๋กœํ‚น๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

@Component
class AsyncComponent{
    @Async
    fun doSomething() {
        println("${Thread.currentThread()}:: start")
        println("${Thread.currentThread()}:: end")
    }
}
@Service
class TestService(
    private val service: AsyncComponent
) {
    fun execute() {
        println("${Thread.currentThread()}:: service start")
        service.doSomething()
        println("${Thread.currentThread()}:: service end")
    }
}

----------------------------------------------------
Thread[Test worker,5,main]:: service start
Thread[Test worker,5,main]:: service end
Thread[junnyland-1,5,main]:: start
Thread[junnyland-1,5,main]:: end
@Component
class AsyncComponent {
    @Async
    fun doResponse(): Future<String> {
        println("${Thread.currentThread()}:: start")
        println("${Thread.currentThread()}:: end")
        return AsyncResult("COMPLETE")
    }
}
@Service
class TestService(
    private val service: AsyncComponent
) {
    fun executeCallback() {
        println("${Thread.currentThread()}:: service start")
        val doResponse = service.doResponse()
        println("${Thread.currentThread()}:: service end : ${doResponse.get()}")
    }
}

----------------------------------------------------
Thread[Test worker,5,main]:: service start
Thread[junnyland-1,5,main]:: start
Thread[junnyland-1,5,main]:: end
Thread[Test worker,5,main]:: service end : COMPLETE

Future.get() ๋ฉ”์†Œ๋“œ๋Š” ๊ฒฐ๊ณผ๊ฐ€ ๋ฐ˜ํ™˜๋  ๋•Œ๊นŒ์ง€ ํ˜„์žฌ ์Šค๋ ˆ๋“œ๋ฅผ ์ฐจ๋‹จ(block)ํ•ฉ๋‹ˆ๋‹ค.

With Transaction

์Šคํ”„๋ง์—์„œ @Async ์–ด๋…ธํ…Œ์ด์…˜๊ณผ @Transactional ์–ด๋…ธํ…Œ์ด์…˜์„ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋ฉด, ์˜ˆ๊ธฐ์น˜ ์•Š์€ ๋™์ž‘์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” @Async ์–ด๋…ธํ…Œ์ด์…˜๊ณผ @Transactional ์–ด๋…ธํ…Œ์ด์…˜์ด ๋™์‹œ์— ์ ์šฉ๋˜๋Š” ๋ฉ”์†Œ๋“œ์˜ ์‹คํ–‰ ์ปจํ…์ŠคํŠธ๊ฐ€ ๋‹ค๋ฅด๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

@Component
class AsyncComponent(
    private val repository: AsyncRepository,
    private val transactionManager: TransactionTemplate
) {
    @Async
    fun doResponse(): Future<String> {
        transactionManager.execute {
            println("${Thread.currentThread()}:: start")
            repository.saveAll() // error!
            println("${Thread.currentThread()}:: end")
            throw RuntimeException("test")
        }
        return AsyncResult("COMPLETE")
    }
}

TransactionTemplate์„ ์‚ฌ์šฉํ•˜์—ฌ ํŠธ๋žœ์žญ์…˜์ด ์˜ˆ๊ธฐ์น˜ ์•Š๊ฒŒ ์ปค๋ฐ‹๋˜์ง€ ์•Š๋„๋ก ํ•ด์ฃผ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

Difference Corrountine

@Async๋Š” Spring Framework์—์„œ ์ง€์›ํ•˜๋Š” ๊ธฐ๋Šฅ์œผ๋กœ, Java์˜ Executor ๋˜๋Š” Java์˜ Thread๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋น„๋™๊ธฐ ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. @Async๋Š” ๋ณ„๋„์˜ ์Šค๋ ˆ๋“œ์—์„œ ๋น„๋™๊ธฐ ์ž‘์—…์„ ์‹คํ–‰ํ•˜๋Š” ๊ฒƒ์œผ๋กœ, ์Šคํ”„๋ง ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋‚ด์—์„œ ๋น„๋™๊ธฐ์ ์œผ๋กœ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ๋ฉ”์†Œ๋“œ๋ฅผ ์ง€์ •ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

Coroutine์€ Kotlin์—์„œ ์ œ๊ณตํ•˜๋Š” ๊ธฐ๋Šฅ์œผ๋กœ, ๋น„๋™๊ธฐ ์ž‘์—…์„ ์œ„ํ•ด ๋…ผ๋ฆฌ์ ์ธ ์Šค๋ ˆ๋“œ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. Coroutine์€ ์ฝ”๋ฃจํ‹ด ์Šค์ผ€์ค„๋Ÿฌ(Coroutine Scheduler)๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋น„๋™๊ธฐ ์ž‘์—…์„ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. ์ฝ”๋ฃจํ‹ด ์Šค์ผ€์ค„๋Ÿฌ๋Š” ์‹ค์ œ ์Šค๋ ˆ๋“œ๋ฅผ ๋งŒ๋“ค์ง€ ์•Š๊ณ ๋„ ๋น„๋™๊ธฐ ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ฃผ๋Š” ๊ธฐ์ˆ ์ž…๋‹ˆ๋‹ค.

How do code

@Component
class AsyncComponent(
    private val repository: AsyncRepository,
) {
    @Transactional
    suspend fun doSomething(): String {
        repository.saveAll() // error!
        "COMPLETE"
        // -> TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()
    }   
}

Last updated