RUNRUN 은 상품에 대한 선착순 구매 기능을 지원하는 서비스입니다.
저희 서비스는 높은 트래픽 상황에서도 유저에게 원활한 구매 경험을 제공하는 것을 목표로 하고 있습니다.
서비스의 특징은 아래와 같습니다.
상품 별 서킷 브레이커를 도입해 시스템 안정성 확보 #92
주문 요청이 폭주되는 상황에서, 시스템의 안정성을 확보하기 위해 상품에 대해 서킷 브레이커를 적용했습니다.
이를 통해 시스템의 신뢰성과 가용성을 유지하고, 대규모 트래픽 상황에서도 안정적으로 서비스를 제공할 수 있습니다.
주문 서버의 자동 확장을 통한 유연한 대응 #111
대규모 트래픽 상황에 적절히 대응하기 위해 주문 서버에 오토 스케일링을 적용했습니다.
이를 통해 트래픽이 급증할 때 서버 리소스를 자동으로 확장하여 서비스 중단 없이 안정적인 주문 처리가 가능하도록 했습니다.
재고 관리에 분산락을 적용해 데이터 무결성 보장 #70
다수의 사용자가 동시에 주문을 시도하는 상황에서 데이터의 무결성을 보장하기 위해 분산 락을 적용했습니다.
이를 통해 경쟁 조건을 방지하고, 정확한 재고 관리와 안정적인 주문 처리가 가능하도록 했습니다.
Deploy |
Language |
Frame Work |
Data Source |
Inter Communication |
Coverage |
ETC |
Version Detail
- Java
22 - Python
3.9 - Gradle
8.8 - Spring Boot
3.3.2 - Kafka
3.8.0 - MySQL
8.0 - MongoDB
7.0.12 - Redis
7.4 - gRPC
1.66.0
- gRPC를 사용하여 서버 간의 직접 통신을 구현했습니다. gRPC는 높은 성능과 낮은 지연 시간을 제공하여, 서비스 간의 데이터 교환을 신속하고 안정적으로 처리할 수 있습니다.
- kafka를 활용하여 서버 간의 간접 통신을 구현했습니다. 이를 통해 더 유연한 데이터 처리가 가능해 시스템의 전체적인 효율성을 향상시켰습니다.
- Redis를 글로벌 캐시 노드로 활용하여, 클러스터 내 모든 노드가 공유하는 캐시를 구현합니다. 이 구조는 스케일링에 적합하며, 모든 노드가 공통된 캐시를 사용함으로써 데이터 일관성을 유지할 수 있습니다.
- Ingress는 외부 트래픽을 관리하고, 모든 외부 요청을 API 게이트웨이로 전달합니다.
- API 게이트웨이는 내부 트래픽을 관리하고, 인증과 인가를 중앙에서 처리하여 시스템의 보안과 일관성을 강화합니다.
- API 게이트웨이의 고가용성을 보장하기 위해 replica 를 설정하여 장애 발생 시에도 시스템의 접근성을 유지합니다.
- HPA를 활용하여 주문 서버와 게이트웨이 서버의 replica 수를 자동으로 조정합니다. 서버의 부하에 따라 적절히 스케일링하여, 높은 트래픽 상황에서도 안정적으로 대응할 수 있습니다.
- CronJob을 활용하여 정기적인 배치 작업을 자동화했습니다. 설정된 시간과 주기에 따라 자동으로 실행되어 데이터 처리 작업을 효율적으로 관리하고, 시스템 운영의 안정성을 높입니다.
┣ 📂 .github
┃ ┗ 📂 workflows
┣ 📂 gateway
┣ 📂 user
┃ ┃ 📂 src/main/resources
┃ ┃ ┗ 📜 application-dev
┃ ┃ ┗ 📜 application-test
┃ ┣ 📜 Dockerfile-dev
┃ ┗ 📜 Dockerfile-test
┣ 📂 brand
┣ 📂 order
┣ 📂 payment
┣ 📂 product
┣ 📂 cron-jobs
┣ 📂 kubernetese
┣ 📂 load-test
┃ ┗ 📜 docker-compose-load-test.yml
┣ 📂 infra
┣ 📜 build.gradle
┣ 📜 settings.gradle
┗ 📜 .gitignore
Description
📂 workflows: 테스트 커버리지 체크와 API 문서 통합 빌드를 자동화하는 워크플로우 파일이 포함되어 있습니다.📂 gateway: 사용자의 인증과 권한 부여를 담당하는 엔드포인트 서버입니다.📂 user: 회원 가입, 로그인, 회원 정보 관리 등을 담당합니다.📂 brand: 상점 생성, 수정, 조회 등 판매자의 상점 관리를 담당합니다.📂 order: 유저의 상품 주문, 조회, 취소, 장바구니 등을 담당합니다.📂 payment: 결제 요청, 결제 취소 등을 담당합니다.📂 product: 상품 등록, 조회, 수정, 삭제 등을 담당합니다.📂 cron-jobs: 주기적으로 실행되는 배치 작업을 담당하는 스크립트를 포함합니다.📂 kubernetese: 쿠버네티스 관련 구성 파일과 스크립트가 모여 있는 폴더입니다.📂 load-test: 부하 테스트 스크립트와 도커 컴포즈 구성 파일을 포함합니다.📂 infra: 서버의 전체 인프라를 구성하는 데 필요한 도커 컴포즈 파일을 포함합니다. 이 폴더는 인프라 배포와 설정을 자동화하는 데 사용됩니다.📜 build.gradle: 하위 모듈에 공통으로 필요한 종속성을 선언합니다. 각 서버 간의 의존성을 최소화하기 위해, 하위 모듈의 테스트 커버리지 통합과 같은 필수적인 작업만을 포함합니다.📜 settings.gradle: 하위 모듈을 선언합니다.📜 .gitignore: git 에 올라가지 않아야 할 파일을 정의합니다.📜 docker-compose-load-test.yml: 부하 테스트 환경을 위한 서버 컨테이너와 모니터링 도구 등을 정의합니다.📂 **/📜application-dev.yml: 개발 환경에 반영될 환경변수를 세팅합니다. 모든 서버 모듈이 포함하고 있습니다.📂 **/📜application-test.yml: 테스트 환경에 반영될 환경변수를 세팅합니다. 모든 서버 모듈이 포함하고 있습니다.📂 **/📜Dockerfile-dev.yml: 개발 환경에 쓰일 도커 이미지를 빌드합니다. 모든 서버 모듈이 포함하고 있습니다.📂 **/📜Dockerfile-test.yml: 테스트 환경에 쓰일 도커 이미지를 빌드합니다. 모든 서버 모듈이 포함하고 있습니다.
상품 별 서킷 브레이커를 도입해 시스템의 안정성을 향상시키고 서버 리소스를 절약했습니다.
- 10,000명의 사용자와 초당 300명의 유저를 가정한 고부하 환경에서 성능 테스트를 진행했습니다.
- 테스트는 주문 요청, 결제 페이지 진입, 결제 요청의 순서로 수행되었습니다.
- 서킷 브레이커 도입 전에는 대규모 사용자가 몰릴수록 주문 서버에서 커넥션 에러가 빈번하게 발생했습니다.
- 전체 에러 중 67%가 처리되지 않은 커넥션 에러였으며, 최대 응답 시간은 약 70초에 달했습니다.
- 주문 서버의 병목 현상으로 인해, 주문 서버와 연동된 결제 API의 응답 시간도 지연되었습니다.
- 상품별 서킷 브레이커를 도입하여, 특정 상품 주문에서 10%의 에러가 발생하면 30초 동안 해당 상품을 포함한 주문에 대해 즉시 에러 응답을 하도록 설정했습니다.
- 메인 주문 로직 이전에 서킷 브레이커가 작동해 빠르게 오류를 감지하고, 불필요한 서버 자원 소모를 줄였습니다.
- 그 결과 커넥션 에러는 1% 이하로 감소했으며, 최대 응답 시간도 30초 이내로 줄어 서킷 브레이커 도입 전보다 50% 향상되었습니다.
| Redis Custom Lock | Redisson Lock | Redis의 싱글 스레드 특성 활용 | |
|---|---|---|---|
| 장점 | - 간단한 구현 |
- Pub/Sub을 활용하여 네트워크 요청 최소화 | - 원자적 작업 보장 - 가장 적은 네트워크 비용 |
| 단점 | - 지속적인 네트워크 요청 필요 |
- Redisson 라이브러리에 대한 학습 필요 | - 코드 복잡도 증가 |
- 데이터 무결성을 보장하기 위해 Redis를 활용해 분산 락을 구현했습니다.
- Redis는 메모리 기반 데이터 저장소로, 매우 빠른 읽기/쓰기 성능을 제공하여 락을 신속하게 획득하고 해제할 수 있습니다.
- MSA 환경에서 노드가 확장될 때, 락 메커니즘을 확장할 수 있어야 합니다.
- 따라서 네트워크 오버헤드가 적고, 클러스터 환경에서도 안정적으로 동작할 수 있는 Redisson 방식을 선택했습니다.
| gRPC | HTTP | |
|---|---|---|
| 장점 | - 인코딩/디코딩 속도가 빠름 - 통신 효율성 높음 |
- 상대적으로 쉬운 기술 도입 |
| 단점 | - 러닝 커브가 있음 - 설정과 학습 필요 |
- 높은 트래픽에서 성능 저하 가능성 |
- MSA 환경은 서버 간 통신이 높은 성능과 낮은 지연 시간을 요구합니다.
- 각 도메인 간 데이터 교환이 빈번하게 이루어지기 때문에, 통신이 느리면 전체 시스템의 응답 시간이 지연될 수 있습니다.
- 따라서 도입에는 러닝 커브가 있지만, 높은 성능과 낮은 지연 시간을 충족할 수 있는 gRPC를 선택했습니다.


