Async
Spring Framework๋ ๋น๋๊ธฐ์ ์ผ๋ก ์คํ๋๋ ์ฝ๋๋ฅผ ์ง์ํ๋ฉฐ, ์ด๋ Spring MVC์ ๊ฐ์ Web Framework์์๋ ์ฌ์ฉํ ์ ์์ต๋๋ค.
Spring์์ ๋น๋๊ธฐ ์ฒ๋ฆฌ๋ฅผ ๊ตฌํํ๋ ๋ฐฉ๋ฒ ์ค ํ๋๋ @Async
์ด๋
ธํ
์ด์
์ ์ฌ์ฉํ๋ ๊ฒ์
๋๋ค.
How do code?
setting gradle, bean
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