Kotlin + Spring MVC 사용 시 Dispatchers 생략
37
작성한 질문수 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
코드의 성능과 효율성은 다양한 요소에 의해 영향을 받습니다. 코드 변경 전/후로 성능 테스트를 통해 검증하는 것이 좋습니다.
기타 궁금한 점이 있으시면 언제든지 질문해 주세요!
inheritance startegy 선택시 고려사항
0
6
0
Entity 동등성 비교
0
14
1
패키지 구분에 대해 궁금한게 있습니다
0
18
2
스프링부트 서버 에러나요
0
20
1
RepositoryTest의 패키지 위치가 domain인 이유
0
30
2
도메인 모델에서 관계와 규칙을 구분하는 방법
0
38
2
16 강의 메세지 retryCount의 의도
0
23
2
JPA Repository 질문이 있습니다!
1
34
2
페이지네이션 처리를 쿼리에서 하는 방식 질문
1
36
1
UserService, CertificationService 책임 분리 기준 질문
0
28
1
다양한 관점의 코드 경험을 위해 개선하지 않은 코드
1
47
1
실무 조언 관련 질문입니다.
0
41
1
sdk 설정 오류
0
50
2
H2데이터베이스 파일 생성
0
51
2
SpringBoot 4.X에서의 Querydsl 설정
0
88
2
강의가 검은 화면으로 나옵니다.
0
50
1
histories() 응답에 PointHistory.id를 포함한 이유가 궁금합니다/
1
44
2
REQUIRES_NEW 해결 방법에 대해서 질문있습니다!!
0
30
1
DI시 eager과 lazy
0
40
2
오탈자 - @Transactional
0
55
1
타입스크립트
0
46
1
Dto와 Entity 사용 관련 질문
0
31
2
서브쿼리 강의에서 ALL 예시 관련 질문드립니다.
0
49
2
EC2 실습 중 docker-compose / docker ps 실행 시 권한 오류 문의
2
71
1





