일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 예외필터
- nodejs
- 자바
- nginx
- 스프링오류
- connection reset by peer
- 동시성문제
- 토스팀
- 대규모 트래픽
- nestjs
- 분산시스템
- 스프링Entity
- 유난한 도전
- 스프링 이미지
- 3WayHandshake
- 토스책
- docker
- OS
- 예외 커스텀
- 스프링jpa
- Mysql이미지
- JavaScript
- nestjs 예외
- 예외 핸들링
- Jenkins
- 스프링
- 트러블슈팅
- 스프링기초
- 동시성 제어
- 동시성 문제
- Today
- Total
삽질블로그
치킨 디도스 프로젝트(3) 본문
목차
1. 프로젝트 개요
- 1-1. 프로젝트 소개
- 1-2. 문제 해결 전략
- 1-3. 기술 스택
- 1-4. 데이터베이스 ERD
2. 성능 개선
- 2-1. 비관적 락 vs 낙관적 락
- 2-2. Redis를 이용한 분산락
- 2-3. Redis 성능 향상
- 2-4. 그래프
3. 개선 사항
1. 프로젝트 개요
1-1. 프로젝트 소개
선착순으로 치킨 쿠폰을 받을 수 있는 치킨 디도스 프로젝트입니다. 첫 번째 분산 시스템 프로젝트입니다.
- 프로젝트 요구사항
쿠폰 발급 이벤트를 진행합니다.
유저는 다음과 같은 종류의 쿠폰 중 하나를 선택하여 발급 받을 수 있습니다.
- 쿠폰 A: 치킨 무료쿠폰, 선착순 300장, 쿠폰코드: A001, 할인율 100%
- 쿠폰 B: 5천원 할인, 선착순 500장, 쿠폰코드: B001, 할인율 50%
- 쿠폰 C: 1천원 할인, 천착순 1000장, 쿠폰코드: C001, 할인율 10%
유저는 선착순으로 쿠폰을 발급받을 수 있습니다. 중복 발급은 불가합니다.
- 이 프로젝트의 핵심 아이디어만 구현하기 위해 다음과 같은 가정을 합니다.
1. MSA 아키텍처라고 가정합니다. 사용자 인증/인가 등의 추가 기능은 포함되지 않습니다.
2. 쿠폰의 정보는 미리 데이터베이스에 들어가 있다고 가정합니다.
1-2. 문제해결전략
치킨 이벤트의 특성을 파악해야 합니다.
- 특정 시간부터 짧은 시간에 막대한 트래픽이 서버에 가해짐
- 짧은 시간에 발생하는 쿠폰 발급은 중복 발급, 제한 수량 등 정확한 데이터가 요구됨
이런 특성을 고려하여, 분산 시스템을 기반으로 한 백엔드 시스템을 구성하였습니다.
- 하나의 쿠폰 발급을 위해 수 많은 유저가 동시에 접근하는 이런 서비스에서는 동시성을 제어하는게 필수적입니다. 따라서 이런 문제를 해결하기 위해 초기에는 외부 자원을 사용하지 않고 데이터베이스의 자체 락을 사용하는 방식을 고려했습니다.
- 하지만 여기서 성능을 더 개선시키기 위해 인메모리 방식인 Redis를 사용하여 분산락을 사용하는 방식으로 수정하였습니다.
1-3. 기술 스택
- Language: Typescript
- Framework: Nest.js
- Database: Mysql
- Cache: Redis, Redlock
- Infra: Docker
- Test: Jest, K6
- Monitoring: Prometheus, Grafana
1-4. 데이터베이스 ERD
2. 성능 개선
10000명의 가상 유저가 60초 동안 300개의 쿠폰을 발급받기 위해 트래픽을 부하할 때를 기준으로 진행했습니다.
2-1. 비관적 락(사용) VS 낙관적 락
이번 프로젝트를 진행하면서 동시성 제어를 해결하기 위해 데이터베이스 자체에서 락을 거는 방식을 고려하였습니다.
동시성을 제어하기 위해서 비관적 락과 낙관적 락 중에서 뭘 사용해야 할까 고려해봤을 때
동시에 수 많은 트래픽이 몰리기 때문에 충돌이 불가피하다고 생각하여 비관적 락을 사용하기로 결정했습니다.
TPS : 139000 / 60 = 2316
MTT : 3.32s
데이터베이스 자체에서 비관적 락을 사용하여 동시성 제어를 했을 때 다음과 같이 발급된 쿠폰 수량이 300개가 정상적으로 발급된 것으로 보아 동시성 제어가 성공적으로 진행된 것을 확인할 수 있습니다.
하지만 각 요청이 완료되는 시간인 MTT가 3.32초로 아쉬운 성능임을 확인할 수 있습니다.
따라서 TPS와 MTT를 개선하기 위해 이보다 더 빠른 캐시를 이용하여 분산락을 통해 성능을 개선하게 되었습니다.
2-2. Redis를 이용한 분산락
TPS : 115000 / 60 = 1916
MTT: 2.26s
Redis를 이용한 분산락을 사용하였을 때 결과를 모니터링한 이미지입니다.
마찬가지로 300개의 쿠폰이 정상적으로 발급된 것으로 보아 동시성 제어가 성공적인 것을 확인할 수 있고
비관적 락에 비해 MTT가 개선된 것을 확인할 수 있지만 TPS가 낮아지는 것을 확인할 수 있습니다.
저는 Redis의 Redlock을 사용했는데 이는 여러 Redis 객체를 이용하게 됩니다.
TPS가 낮아지는 이유가 Redis lock 객체를 초기화할 때 해둔 초기 설정을 할 때
락을 획득하기까지의 과정에 불필요한 리소스 낭비가 있다고 판단하여
초기 설정을 조절하여 네트워크 오버헤드를 줄이고 다음과 같은 개선을 하였습니다.
2-3. Redis 성능 향상
TPS : 198000 / 60 = 3300
MTT : 964.34ms
Redis lock 객체를 초기화할 때 락을 획득하기 위한 재시도 횟수를 줄이고, 재시도 사이에 대기 시간과 재시도 시 더해지는 랜덤 시간을 조절하였습니다. 결과적으로 TPS가 3300, MTT가 1초 미만으로 성능이 많이 좋아진 것을 확인할 수 있습니다.
2-4. 그래프
3. 개선 사항
3-1. k8s를 통한 로드밸런싱
현재 이 서버의 자원을 통해 어떻게 성능을 더 향상시킬 수 있을까 생각한 결과 k8s를 사용하여 서버의 pod를 늘려 로드밸런싱을 활용하면 성능을 향상시킬 수 있을까? 하는 생각에 진행해봤지만 MTT는 향상되는 반면 TPS가 낮아지는 것을 확인하였습니다.
추후에 이를 모니터링하여 어떤 부분에서 문제가 발생하는지 확인하고 개선하는 것을 목표로 하고있습니다.
3-2. Message Queue 활용
메시지 큐를 활용한 대기열 처리를 목표로 하고 있습니다.
3-3. CPU 사용률 감소
현재 CPU 사용률이 극적으로 증가하는 것을 확인하였는데, 이를 모니터링하여 개선하는 것을 목표로 하고 있습니다.
3-4. Redis Cache 사용
Redis를 이용하여 분산락을 사용하고 있지만 캐시를 사용하고 있지 않아서 DB에 부하가 예상되는 상황입니다.
이를 Cache를 통해 데이터에 접근할 수 있도록 바꿔 성능을 개선하는 것을 목표로 하고 있습니다.
'개발' 카테고리의 다른 글
치킨 디도스 프로젝트(2) (2) | 2024.09.03 |
---|---|
치킨 디도스 프로젝트(1) (0) | 2024.08.02 |
NestJS 예외 핸들링 (1) | 2024.07.22 |
개인 프로젝트 후기 (3) | 2024.07.19 |
Docker와 Jenkins를 이용해서 CI/CD를 구축해보자 (1) | 2024.04.16 |