== 상황 ==
현재 배달 서비스는 MSA 아키텍처로 아래와 같이 도메인이 구분 되어 있다.
- 고객 도메인
- 장바구니 도메인
- 상품 도메인
- 매장 도메인
- 결제 도메인
그리고 결제는 외부 PG 사의 API 를 활용하기에 주문 요청과 결제 성공이라는 요청을 구분해 전달 받아야하며 전체적인 순서는 아래와 같다.
[ 주문 요청 ]
- 주문 요청
- 요청 검증
- 결제 요청
- 외부 PG사에서 결제 URL 및 TID 반환
- 결제 성공시 결제 승인을 위한 Token 반환
[ 결제 성공 ]
- 결제 후 PG사에서 발급 받은 TID, Token 과 함께 결제 승인 요청
- 결제 완료
- 주문 데이터 저장 및 주문 성공
== 문제 ==
위와 같은 과정 중 외부PG 사의 정보는 직접적으로 관리할 수 없는 만큼 결제 완료 이전 과정에서 모든 검증이 마무리 되어야 하는 문제가 발생하였다.
또한 외부 PG사의 통신은 외부 통신을 활용하는 만큼, 고정적인 지연 시간(병목 현상)이 발생하였고, 때문에 앞선 과정에서의 처리 속도를 향상 시킬 필요가 있었다.
앞선 검증 과정에서는 아래와 같은 확인이 필요했다.
- 유효한 고객의 요청인지 확인
- 고객의 장바구니 리스트를 조회
- 장바구니 내의 상품이 유효한지 확인
- 장바구니 내의 매장이 유효한지 확인
- 결제 도메인으로 결제 요청 (외부PG사 통신)
== 고민 ==
외부 PG사의 통신 시간은 개선할 수 없는 고정적인 지연 시간인 만큼, 자바의 Virtual Thread 를 활용해 검증 과정을 비동기로 처리하는 방안을 고민했다.
또, 이러한 과정에서 병목 구간이 되는 장바구니 리스트 조회 과정을 제외하고 장바구니 정보는 클라이언트의 요청 값에 포함 시키기로 하였다.
장바구니 정보를 클라이언트에서 직접 전달 받는 만큼 병목 현상은 해소할 수 있었지만, 클라이언트에서 요청을 조작할 수 있다는 문제점이 있었기에 전달 받은 요청과 실제 장바구니 값이 일치하는지 검증 과정을 포함하게 되었다.
이러한 방법을 도입해 처리량을 약 130% 개선할 수 있었지만, 하나의 요청에 5개의 가상 스레드가 생성되는 만큼 요청이 많아질 수록 이를 처리하기 위한 비용이 증가 되었다.
또, 도메인 간 결합이 강해 다른 도메인에 장애가 발생하면 처리 자체가 불가능해지는 MSA 의 목적과는 부합하지 않는 방법이라는 문제점이 있었다.
== 조치 ==
최종적으로 Kafka 의 메세지 큐를 활용해 이벤트 기반(Event-Driven) SAGA 패턴을 활용하기로 하였다.
동작 순서는 아래와 같다.
[ 주문 요청 ]
[ 결제 성공 ]
위와 같은 방법으로 동기적으로 처리하는 방법과 비교해 응답 속도를 약 200% 이상 개선할 수 있었다.
테스트 결과는 아래와 같다.
단일 요청 ( 100 번 ) | OpenFeign | OpenFeign + VirtualThread | SAGA pattern |
http_req_duration | 460.28ms | 218.68ms | 166.66ms |
다중 요청 5s 200명 | OpenFeign | OpenFeign + VirtualThread | SAGA pattern |
http_req_duration | 4.7s | 2.15s | 1.64s |
이러한 SAGA pattern 과 VirtualThread 를 함께 쓰는 방안도 테스트를 해보았으나 동시에 처리하는 요청이 많아질 수록 VirtualThread 사용 유무에 따른 차이를 확인하기 어려웠다.
I/O 작업이 하나 밖에 없는 작업에서 과도한 가상 스레드 생성으로 인한 오버헤드가 원인으로 예상이 되었지만 일정 요청까지는 20~30% 이상의 성능적 이점을 확인할 수 있었고, 쿠버네티스의 오토 스케일링으로 하나의 서버에 과도한 요청이 전달될 가능성은 낮다 판단하였기에 최종적으로 해당 방안을 도입하기로 하였다.
다만, 위와 같은 주문 요청 과정에서 각 도메인으로 전달한 검증 요청 메세지가 유실될 경우 주문이 무기한 처리 되지 않는 문제가 예상 되었다.
이러한 문제는 주문 요청 시간부터 일정 시간 처리가 되지 않으면, 주문 실패로 처리하는 방향으로 처리하고자 하며, 이때 지연 메세지 큐를 활용할 예정이었으나 Kafka 에서는 기본적으로 지연 큐를 지원하지 않아 아래와 같은 방법을 고려 중이다.
- RabbitMQ 를 도입해 지연 큐 활용
- Redis 의 TTL 이벤트와 Kafka Connect 를 활용
관련 내용은 자료 조사 후 다른 글에서 다뤄보도록 하겠다.
'기술적 고민' 카테고리의 다른 글
동시성 제어에 캐시와 분산락 Redisson 을 도입한 이유 (1) | 2025.04.09 |
---|---|
예약 관리 서비스에 RabbitMQ 를 도입하게 된 이유 (0) | 2025.03.26 |
데이터 유실 방지 - 외부 저장소(S3) 장애 대응 (0) | 2025.03.04 |
무한스크롤에서 Offset 방식의 문제와 Keyset Pagination 사용 이유 (1) | 2025.02.20 |
메타데이터를 이용한 객체 값 자동 생성 ( DatabaseMetaData ) (0) | 2024.10.08 |