일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- 토스책
- 유난한 도전
- 토스팀
- 동시성문제
- 스프링
- 대규모 트래픽
- connection reset by peer
- 자바
- nestjs
- 스프링기초
- 스프링Entity
- Jenkins
- 3WayHandshake
- 예외 커스텀
- 예외필터
- docker
- nestjs 예외
- nodejs
- 트러블슈팅
- 스프링 이미지
- nginx
- 스프링jpa
- 예외 핸들링
- JavaScript
- 동시성 문제
- 분산시스템
- Mysql이미지
- 스프링오류
- OS
- 동시성 제어
- Today
- Total
삽질블로그
치킨 디도스 프로젝트(2) 본문
목차
1. 프로젝트 개요
- 1-1. 프로젝트 소개
- 1-2. 문제 해결 전략
- 1-3 기술 스택
2. 개발 결과물
- 2-1. 데이터베이스 ERD
- 2-2. 백엔드 아키텍처
- 2-3. 부하 테스트
3. 개선 사항
- 3-1. 백엔드 아키텍처
- 3-2. TPS, MTT 개선
4. 후기
1. 프로젝트 개요
1-1. 프로젝트 소개
선착순으로 치킨 쿠폰을 받을 수 있는 치킨 디도스 프로젝트입니다. 첫 번째 분산 시스템 프로젝트입니다.
- 프로젝트 요구사항
쿠폰 발급 이벤트를 진행합니다.
유저는 다음과 같은 종류의 쿠폰 중 하나를 선택하여 발급 받을 수 있습니다.
- 쿠폰 A: 치킨 무료쿠폰, 선착순 300장, 쿠폰코드: A001, 할인율 100%
- 쿠폰 B: 5천원 할인, 선착순 500장, 쿠폰코드: B001, 할인율 50%
- 쿠폰 C: 1천원 할인, 천착순 1000장, 쿠폰코드: C001, 할인율 10%
유저는 선착순으로 쿠폰을 발급받을 수 있습니다. 중복 발급은 불가합니다.
- 이 프로젝트의 핵심 아이디어만 구현하기 위해 다음과 같은 가정을 합니다.
1. MSA 아키텍처라고 가정합니다. 사용자 인증/인가 등의 추가 기능은 포함되지 않습니다.
2. 쿠폰의 정보는 미리 데이터베이스에 들어가 있다고 가정합니다.
1-2. 문제해결전략
치킨 이벤트의 특성을 파악해야 합니다.
- 특정 시간부터 짧은 시간에 막대한 트래픽이 서버에 가해짐
- 짧은 시간에 발생하는 쿠폰발급은 중복발급, 제한 수량 등 정확한 데이터가 요구됨
이런 특성을 고려하여, 분산 시스템을 기반으로 한 대규모 백엔드 시스템을 구성하였습니다.
- 수 많은 트래픽을 감당하기 위해 수평적인 확장이 가능한 무상태 API 서버로 개발하였습니다. 또한 특정 시간에 무수한 트래픽이 몰리기 때문에, 오토 스케일링이 아닌 이벤트 시작 전에 많은 서버를 미리 올려 놓는 형태로 진행하였습니다. 이와 같은 상황에서 사용하게 될 수 많은 서버들을 관리하기 위해 k8s를 사용하였습니다.
- 이와 같은 대규모 트래픽에서는 동시성 문제가 발생하기 때문에 동시성 제어를 수행해야 합니다. 첫 번째 고려사항으로 데이터베이스의 낙관적 락과 비관적 락을 고려하였지만, 짧은 시간 안에 발생하는 수 많은 트랜잭션을 처리하기 위해 이보다 빠른 캐시를 사용하여 Lock을 거는 방식을 선택했고 분산 시스템에 맞춰 분산락을 사용하였습니다.
이런 특성들을 고려하여 다음과 같은 기술을 적용하였습니다.
- 캐시를 이용한 Lock을 걸기 위해 Redis를 사용하였으며, 단일 Redis에서 구현하는 분산락의 방법도 있지만 레디스 노드에 장애가 발생한다면 모든 서비스에서 레디스를 사용할 수 없게 되고 따라서 전체 프로그램의 장애로 연결되는 치명적인 에러가 발생할 수 있기 때문에Redis의 여러 객체를 이용하는 Redlock을 사용하였습니다.
1-3. 기술 스택
- Language: Typescript
- Framework: Nest.js
- Database: Mysql
- Cache: Redis, Redlock
- Infra: Docker, k8s
- Test: Jest
2. 개발 결과물
2-1. 데이터베이스 ERD
데이터베이스는 쿠폰 정보를 가지고 있는 coupon테이블과, 발급 받은 사용자와 쿠폰에 대한 정보를 확인할 수 있는 coupon_wallet테이블로 구성하였습니다. 쿠폰 code와 지갑 id같은 경우 추후에 DB와 Cache의 Replica를 고려하여 중복을 피하기 위해 UUID로 구성하였습니다.
2-2. 백엔드 아키텍처
k8s기반으로 아키텍처를 구성하였습니다. API 서버, 캐시, 데이터베이스 모두 컨테이너화되어 pod로 존재하도록 구성하였으며,
각 pod들은 단일 Service에 연결하였습니다. 가장 앞단에서 트래픽을 받아낼 Coupon-Api-Service, Coupon-Api-Server는
Ingress에 연결하여 외부의 트래픽을 받을 수 있도록 하였습니다.
특히, Redis를 이용한 분산락에서 Redlock 라이브러리의 특성 때문에 redis의 pod를 3개로 구성하였습니다.
2-3. 부하 테스트
k6를 이용해 10000번의 유저가 60초 동안 부과하는 트래픽을 서버에서 받을 때에도,
쿠폰의 제한수량인 300장만 발급 받은 것을 확인할 수 있습니다.
이를 통해 분산 락을 통한 동시서 제어가 정상적으로 동작하는 것을 알 수 있습니다.
3. 개선사항
3-1. 백엔드 아키텍처
막대한 트래픽이 부과되는 중에 Message Queue를 이용해 대기열 처리를 하는 것을 목표로 하고 있습니다.
3-2. TPS, MTT 개선
현재 k8s를 통해 서버를 부하 분산 시킬 시 단일 서버에 비교해 MTT는 상당히 개선되었지만 TPS의 성능이 낮아지는 것을
모니터링하여 확인하였습니다. 후에 k8s를 모니터링하면서 TPS를 개선하는 것을 목표로 하고 있습니다.
4. 후기
이번 프로젝트를 진행하면서 동시성 제어를 수행하게 되었는데, 데이터베이스의 락이 어떤 종류가 있는지 공부하고 실제로 적용해볼 수 있는 기회가 되었습니다. 또한 테스트 코드를 유닛 테스트부터 e2e테스트까지 진행하면서 어떻게 더 좋은 테스트 코드를 작성할 수 있을까 고민해볼 수 있었습니다.
특히 대규모 트래픽을 발생시킬 때 클라이언트에서 발생하는 connection reset by peer가 발생했는데 이에 대해 와이어샤크로 패킷을 분석하면서 3-way handshake에 대해 더 이해할 수 있게 되었고 더 깊이 들어가 운영체제의 시스템 호출, nodejs가 커널을 어떻게 이용하는지 조금의 고찰을 할 수 있는 좋은 기회가 되었습니다.
'개발' 카테고리의 다른 글
치킨 디도스 프로젝트(3) (3) | 2024.09.04 |
---|---|
치킨 디도스 프로젝트(1) (0) | 2024.08.02 |
NestJS 예외 핸들링 (1) | 2024.07.22 |
개인 프로젝트 후기 (3) | 2024.07.19 |
Docker와 Jenkins를 이용해서 CI/CD를 구축해보자 (1) | 2024.04.16 |