Kotlin + Spring MVC 사용 시 Dispatchers 생략
58
작성한 질문수 1
안녕하세요~
현재 아래 기술 스택을 사용한 API 서버가 존재합니다.
Spring Boot 3
Kotlin
JPA(JDBC)
WebClient
예를 들면 아래와 같이 구성되어 있고,
@RestController
class SampleController(
private val service: SampleService
) {
@GetMapping("/sample")
suspend fun start() {
service.withoutDispatchers()
}
}
@Component
class SampleClient {
private val webClient: WebClient by lazy {
WebClient.builder()
.baseUrl("https://api.com")
.build()
}
suspend fun fetch(): Int {
return webClient.get()
.uri { uriBuilder ->
uriBuilder.path("/number").build()
}
.retrieve()
.awaitBody()
}
}
interface SampleRepository : JpaRepository<SampleEntity, Long>@Service
class SampleService(
private val client: SampleClient,
private val repository: SampleRepository,
) {
suspend fun withoutDispatchers(): Int = coroutineScope {
// non-blocking
val deferred: Deferred<Int> = async {
client.fetch()
}
// blocking
val entity: List<SampleEntity> = repository.findAll()
deferred.await() + entity.size
}
}
Controller에서부터 suspend function으로 시작되면 Dispatchers.Unconfined를 사용하여 API 요청에 의해 할당받은 Tomcat 스레드를 그대로 사용하는 것으로 알고 있습니다.
이후 Service 레이어에서 suspend function이 동일하게 Dispatchers.Unconfined를 유지하기 때문에 요청 시 Tomcat 스레드는 그대로 사용되고
WebClient 요청 시 Dispatchers.IO를 사용하면,
Tomcat 스레드와 별개로 Dispatchers 스레드 풀에서 스레드를 가져와 사용하는 것으로 알고 있습니다. 1번의 요청에서 Tomcat 스레드 1개 + Dispatchers 스레드 1개 = 총 2개가 사용됩니다.
WebClient 요청 시 Dispatchers.IO를 생략하고 Dispatchers.Unconfined를 유지하면,
코루틴의 재개 시 WebClient 응답을 동일한 Tomcat 스레드가 처리하게 하여 스레드 1개만을 사용하는 것이 서버에 더 효율적이지 않을까? 하는 생각이 들었습니다.
스레드가 Blocking되어도 WebClient의 NIO 이벤트 루프에 들어온 요청은 처리되어 응답에 대한 처리만 대기하고 있을 것이고, 테스트해보았을 때도 WebClient의 요청을 시작한 스레드가 JPARepository를 사용하는 코드에 의해 Blocking되어도 await()에서 문제 없이 응답을 받아올 수 있었습니다.
혹시 제가 생각하지 못한 또 다른 문제가 있을까요?
제가 혹시 잘못 알고 있는 부분이 있거나 Dispatchers.IO를 생략하지 못 하는 이유가 있다면
설명해주시면 감사하겠습니다!
답변 1
0
안녕하세요, 인프런 AI 인턴입니다.
kbm0598님,
질문하신 내용에 대해 답변드리겠습니다.
Kotlin의 코루틴과 Spring Boot에서 WebClient, JPA를 사용하는 시나리오에서는 코루틴 컨텍스트와 그에 따른 스레드 관리를 잘 이해해야 효율적인 성능 최적화를 이룰 수 있습니다.
Dispatchers.Unconfined 사용:
Dispatchers.Unconfined는 코루틴 재개 시점에 호출한 스레드에서 지속되며, 주로 I/O 처리가 없는 간단한 작업에 적합합니다. 따라서 WebClient와 같은 비동기 HTTP 호출이나 JPA와 같은 데이터베이스 작업에서는 적절하지 않을 수 있습니다. 각 작업에 맞는 최적화된 Dispatchers 사용이 중요합니다.JPA(JDBC)와의 호환성 문제: JPA는 기본적으로 blocking I/O 작업을 수행합니다. 이 경우
Dispatchers.IO를 사용하여 blocking I/O를 별도의 스레드 풀로 옮기는 것이 일반적입니다. 그렇게 하지 않을 경우 스레드가 blocking되어 전체 응답 시간을 지연시킬 수 있습니다. Tomcat 스레드를 계속 사용하는 것은 서버 부하를 유발할 가능성이 있습니다.WebClient 사용 시의 Dispatchers:
WebClient의 경우, 내부적으로 non-blocking I/O를 사용하기 때문에 굳이Dispatchers.IO를 사용하지 않아도 괜찮습니다. 하지만 마찬가지로 스레드를 효율적으로 관리하는 것이 중요합니다.성능 테스트 결과: 귀하의 테스트에서 문제를 발견하지 못했더라도, 트래픽 상황, 부하 테스트 등 다양한 조건에서 성능을 정밀하게 검토할 필요가 있습니다. 실제 프로덕션 환경에서는 동시 사용자 수 증가 시 리스크가 발생할 가능성을 항상 염두해야 합니다.
추가적으로, 아래의 Spring Reactive와 관련한 공식 문서를 참고하시면 도움이 될 것입니다: Spring WebFlux Reference
코드의 성능과 효율성은 다양한 요소에 의해 영향을 받습니다. 코드 변경 전/후로 성능 테스트를 통해 검증하는 것이 좋습니다.
기타 궁금한 점이 있으시면 언제든지 질문해 주세요!
해결했습니다
0
12
1
미션 제출 방식 건의
0
10
0
트랜잭션 내 Redis I/O 에 대한 궁금한점
1
15
1
미션 진행 방식에 대한 질문입니다.
0
18
1
지금 강의가 어려우신분 추천드립니다.
0
46
1
기존의 완성된 CRUD의 Build.gradle에 security관련 dependency를 추가하면 문제가 생깁니다.
0
59
2
파킹 페이지와 대기열 큐 질문입니다.
0
37
1
조회 메서드에서 @Transactional(readOnly=true) 질문
1
39
1
"폴더를 찾을 수 없습니다." 라는 응답만 계속 나옵니다.
0
42
1
EmailSender 관련 문의
0
50
1
CRA ? 질문입니다
0
46
1
[해결됨]마지막 강의 내용 수정 화면이 기능을 못하네요.
0
47
1
[해결됨]부분적으로 화면 이동이 안되는 부분이 있습니다.
0
84
6
해당 결제 부분을 포폴에서 테스트 api를 적용에 대한 궁금증
1
44
1
aws 계정 생성 질문
1
64
1
조회 메서드 네이밍 질문
0
59
1
소스파일
0
44
1
음성불량
0
48
1
11강 [실습] Docker로 MySQL1 분만에 실행하기
0
86
2
11강 [실습] Docker로 MySQL1 분만에 실행하기
0
63
2
구현 방법에 대한 문의
0
72
2
장애 격리 관련해서 궁금한 부분이 있어요
0
55
2
질문있습니다.
0
49
1
MSA 통신방식에 대해서 질문 있습니다.
0
50
1





