어흥
[대규모 시스템 설계] 4. 처리율 제한 장치의 설계 - 2 본문
3단계: 상세 설계
개략적인 설계에서 알 수 없는 점
- 처리율 제한 규칙은 어떻게 만들어지고 어디에 저장되는가?
- 처리가 제한된 요청들은 어떻게 처리되는가?
리프트(Lyft)는 처리율 제한에 오픈 소스를 사용한다.
domain: messaging
descriptions:
- key: message_type
Value: marketing
rate_limit:
unit: day
requests_per_unit: 5
이런 규칙들은 보통 설정 파일(configuration file) 형태로 디스크에 저장된다
처리율 한도 초과 트래픽의 처리
어떤 요청이 한도 제한에 걸리면 API는 HTTP 429(Too many requests) 응답을 클라에게 보낸다. 시스템 과부하로 인해 처리 못한 경우, 한도 제한에 걸린 메시지를 나중에 처리하기 위해 큐에 보관할 수 있다.
그렇다면 클라는 자신의 요청이 처리율 제한에 걸리고 있는지(throttle) 어떻게 감지하는가? 자신의 요청이 처리율 제한에 걸리기까지 얼마나 많은 요청을 보낼 수 있는지 어떻게 알 수 있을까?
HTTP 헤더에 답이 있다
- X-Ratelimit-Remaining: 윈도 내 남은 처리 가능 요청의 수
- X-Ratelimit-Limit: 매 윈도마다 클라이언트가 전송할 수 있는 요청의 수
- X-Ratelimit-Retry-After: 한도 제한에 걸리지 않으려면 몇 초 뒤에 요청을 다시 보내야 하는지 알려줌
사용자가 너무 많은 요청을 보내면 API는 429 오류를 X-Ratelimit-Retry-After 헤더와 함께 반환한다
상세 설계
- 처리율 제한 규칙은 디스크에 보관하며 작업 프로세스는 수시로 규칙을 디스크에서 읽어 캐시에 저장한다
- 클라가 요청을 서버에 보내면 처리율 제한 미들웨어에 먼저 도달한다
- 처리율 제한 미들웨어는 제한된 규칙을 캐시에서 가져온다. 그리고 레디스 캐시에서 카운터와 마지막 요청에 대한 타임스탬프를 가져온다.
위 값들을 바탕으로 해당 요청이 처리율 제한에 걸리지 않으면 API로 보내고, 걸린다면 429 에러를 클라에게 보내고 해당 요청을 버리거나 메세지 큐에 보관한다
분산 환경에서의 처리율 제한 장치의 구현
여러 대의 서버와 병렬 스레드를 지원하도록 시스템을 확장하는 것은 2가지 어려운 문제를 풀어야 한다
1. 경쟁 조건(Race condition)
발생하는 이유:
- 레디스에서 카운터의 값을 읽는다
- 카운터의 값에 1을 더해도 임계치를 넘지 않는지 확인한다
- 넘지 않는다면 1을 증가시켜서 저장한다
경쟁 조건 문제를 해결하기 위한 방법은 3가지가 있다.
- 락 : 시스템의 성능을 상당히 떨어뜨리기 때문에 잘 사용하지 않는다
- 루아 스크립트: 이벤트 기반 프로그래밍의 경량 언어로 단일 스레드 환경에서 동작하며 순차적으로 실행된다. 상호 배제를 통해 여러 작업이 동시에 데이터를 수정하지 않는다
- 정렬 집합이라고 불리는 레디스 자료구조 사용: MULTI/EXEC 명령어를 사용하여 트랜잭션 사용이 가능하고, 레디스 Lock도 존재한다. 또한 Pub/Sub 메커니즘을 사용하여 이벤트 기반 구조로 설계하고 해당 이벤트 발생시 다른 클라는 구독을 통해 변경을 감지하고 처리할 수 있다.
2. 동기화 이슈(Synchronization)
수백만 사용자를 지원하려면 한 대의 처리율 제한 장치 서버로는 충분하지 않을 수 있다. 그래서 처리율 제한 장치 서버를 여러 대 두게 되면 동기화가 필요하다
웹 계층은 무상태라서 처음 요청은 왼쪽처럼, 이후 요청은 오른쪽처럼 진행될 수 있다. 이때 동기화를 하지 않았다면 처리율 제한 장치는 제한을 올바르게 수행할 수 없다.
이에 대한 해결책으로 고정 세션을 활용하여 같은 클라로부터의 요청은 항상 같은 처리율 제한 장치로 보낼 수 있도록 하는 것이 있지만, 규모면에서 확장 가능하지도 않고 유연하지 않기 때문에 추천하지 않는다.
아래와 같이 레디스와 같은 중앙 집중형 데이터 저장소를 사용하는것이 더 좋은 방법이다.
성능 최적화
위 설계에서 2가지 지점에서 개선이 가능하다
1. 여러 데이터센터를 지원하는 문제는 처리율 제한 장치에 매우 중요한 문제다. 멀리 떨어진 사용자를 지원하려다보면 지연시간이 증가할 수밖에 없는데 이때 CDN과 같은 에지 서버를 통해 지연시간을 줄이도록 한다
2. 제한 장치 간에 데이터를 동기화할 때 최종 일관성 모델(Eventual Consistency Model)을 사용한다
모니터링
처리율 제한 장치를 설치한 이후에는 효과적으로 동작하고 있는지 보기 위해 데이터를 모을 필요가 있다. 모니터링을 통해선 다음을 확인한다
- 채택된 처리율 제한 알고리즘이 효과적이다(이동 윈도 카운터, 이동 윈도 로그 등)
- 정의한 처리율 제한 규칙이 효과적이다(카운터의 수, 버킷의 크기 등)
상황에 따라 처리율 제한 규칙을 변경할 필요도 있는데, 이벤트 때문에 트래픽이 급증하는 경우 토큰 버킷이 적합한 알고리즘이다.
4단계: 마무리
학습한 알고리즘
- 토큰 버킷
- 누출 버킷
- 고정 윈도 카운터
- 이동 윈도 로그
- 이동 윈도 카운터
시간이 남는다면 아래와 같은 부분을 언급해도 괜찮다
1. 경셩(Hard) 또는 연셩(Soft) 처리율 제한
- 경성 처리율 제한: 요청의 개수는 임계치를 절대 넘을 수 없다
- 연성 처리율 제한: 요청 개수는 잠시 동안은 임계치를 넘을 수 있다
2. 다양한 계층에서의 처리율 제한
이번 장에서는 애플리케이션 계층(OSI 7계층 기준 7계층)에서 다뤘다. Iptables를 사용하면 IP 주소에(3계층) 처리율 제한을 적용할 수 있다
3. 처리율 제한을 회피하는 방법
- 클라이언트 캐시를 사용하여 API 호출 횟수를 줄인다
- 처리율 제한의 임계치를 이해하고, 짧은 시간 동안 많은 메세지를 보내지 않도록 한다
- 예외나 에러를 처리하는 코드를 도입하여 클라가 예외적 상황으로부터 우아하게 복구될 수 있도록 한다
- 재시도 로직을 구현할 때는 충분한 백오프 시간을 둔다
'개발 > 대규모 시스템 설계' 카테고리의 다른 글
[대규모 시스템 설계] 6. 키-값 저장소 설계 - 1 (0) | 2023.08.16 |
---|---|
[대규모 시스템 설계] 5. 안정 해시 설계 (0) | 2023.08.14 |
[대규모 시스템 설계] 4. 처리율 제한 장치의 설계 - 1 (0) | 2023.08.02 |
[대규모 시스템 설계] 3. 시스템 설계 면접 공략법 (0) | 2023.07.31 |
[대규모 시스템 설계] 2. 개략적인 규모 추정 (0) | 2023.07.31 |