[시스템 디자인] 04. 분산 메시지 큐
[시스템 디자인] 04. 분산 메시지 큐
시스템 디자인 시리즈의 글입니다.
시스템 설계 면접계의 단골, 분산 메세지 큐.
시스템 블록 사이의 통신과 조율을 담당하는 분산 메세지 큐를 설계해보자
0. 분산 메세지 큐
장점
- 결합도 완화(decoupling) : 컴포넌트간 디커플링
- 규모 확장성 개선 : 트래픽에 맞게 생산자/소비자를 독립적으로 늘릴수있음
- 가용성 개선 : 특정컴포넌트 장애가 다른 컴포넌트로 이어지지 않음
- 성능 개선 : 비동기 통신이 쉽게 가능. 생산자/소비자가 서로 기다리지 않음.
유명 분산 메세지 큐
- 아파치 카프카
- 아파치 RocketMQ
- 아파치 RabbitMQ
- 아파치 펄사
- 아파치 ActiveMQ
- ZeroMQ
메세지 큐 대 이벤트 스트리밍 플랫폼
지원하는 기능이 서로 수렴하면서, 두 플랫폼 경계는 희미해지고잇음
- 이벤트 스트리밍 플랫폼 : 카프카, 펄사
- 메세지큐 : RocketMQ, ActiveMQ, RabbitMQ, ZeroMQ
1. 문제 이해 및 설계 범위 확정
기능 요구사항
- 생산자는 메세지 큐에 송신가능
- 소비자는 메세지큐에서 수신가능
- 메세지 반복수신 / 한번만 수신 설정 가능
- 오래된 이력 데이터 삭제 가능
- 메세지크기 KB수준
- 메세지가 생선된 순서대로 소비자에게 전달될 수 있어야함
- 전달방식 최소 한 번, 최대 한 번, 정확히 한 번 가운데 설정 가능
비기능 요구사항
- 높은 대역폭 or 낮은 전송 지연 설정으로 선택 가능
- 규모 확장성
- 지속성 및 내구성
전통적 메세지큐와 다른점
전통적인 큐는 메세지 보관문제를 중요하게 다루지 않음. 소비자에 전달되기 전에 메모리 보관만 충분히 하다가 삭제함. 또한 메세지 전달 순서도 보존하지않음. → 설계가 단순해질수있다
2. 개략적 설계안 제시 및 동의 구하기
- 생산자는 메세지를 큐에 발행
- 소비자는 큐를 구독, 메세지 소비
- 큐는 둘 사이 결합을 느슨하게하여 독립적 운영 및 scaling을 가능하게함
- 메세지큐=서버, 생산자, 소비자=클라
메세지 모델
- 일대일 모델(point to point)
- 전통적 큐의 일반적 모델.
- 큐에 전송된 메세지는 오직 한 소비자만 가져감
- 소비자가 메세지를 가져갔다는 사실을 알리면 메세지는 큐에서 삭제됨
- 데이터보관 지원X
- 발행-구독 모델(publish-subscribe)
- 토픽(메세지 주제별로 정리) 사용
- 토백을 구독하는 모든 소비자에게 메세지 전달
- 소비자들은 소비자그룹으로 묶이게됨
토픽, 파티션, 브로커
- 메세지 데이터 양이 서버 한 대로 감당하기 힘들수 있음. → 파티션(샤딩) 기법 활용
- 토픽을 여러 파티션으로 분할하여 메세지를 파티션에 균등하게 나눠 보냄
- 파티션은 큐 클러스터 서버에 고르게 분산배치됨
- 브로커(broker)는 파티션을 유지함
- 파티션은 FIFO로 동작
- 파티션 내 메세지 위치는 오프셋(offset)으로 관리
- key를 사용해 특정 파티션에만 메세지를 보낼 수 있음
- 토픽을 구독하는 소비자는 하나 이상의 파티션에서 데이터를 가져옴
- 소비자가 여럿이면 각 구독자는 특정 파티션을 담당하며 메세지를 가져옴 (=소비자그룹)
소비자 그룹
- 일대일/발행-구독모델 모두 지원.
- 소비자 그룹은 여러 토픽을 구독할수있고, 오프셋을 별도로 관리
- 소비자들은 메세지를 병렬로 소비
- 병렬로 읽을시 대역폭은 좋지만 순서보장을 할수없음
- 한 파티션 - 한 소비자로 매핑하면 순서보장 가능하긴함.
- 일대일 모델 수렴
- 단, 소비자 수가 파티션 수 보다 많으면 놀고있는 소비자 생김
개략적 설계안
- 클라이언트
- 생산자 : 메세지송신
- 소비자 : 메세지 수신
- 핵심서비스 및 저장소
- 브로커 : 파티션 유지
- 저장소
- 데이터 저장소 : 메세지저장
- 상태저장소 : 소비자상태 저장
- 메타데이터 저장소 : 토픽관련정보 저장
- 조정서비스 : 서비스 탐색, 리더선출 등 담당
3. 상세설계
- 회전디스크(rotational disk) 사용. : 높은 순차 탐색 성능, 적극적 디스크 캐시 전략 사용
- 메세지가 아무 수정 없이 전송 가능하도록 설계. (메세지 복사 비용 최소화)
- 일괄처리(batching)를 우선하여 설계.
데이터 저장소
메세지 큐의 트래픽패턴
- 읽기와 쓰기 빈번
- 갱신/삭제연산 없음
- 순차적 읽기/쓰기가 대부분.
선택지
- RDB : 오히려 시스템 병목이됨
- WAL (Write-Ahead Log 쓰기 우선로그)
- append only
- 지속성을 보장할때 굿.
- 순차적 읽기/쓰기에 디스크는 좋은성능을보임
- 회전식디스크기반 = 큰용량을 저렴한 가격에 제공
- 세그먼트단위로 나눠서 관리하면 용량제한도 없음
- 디스크캐시 적극 활용
메세지 자료 구조
- 메세지구조는 높은 대역폭 달성의 열쇠
- 메세지 자료구조 = 생산자, 큐, 소비자 사이의 계약(contract)
- 불필요한 복사가 없어야 높은 대역폭 달성가능
- 스키마
- 메세지키 : 파티션 정할때 사용
- 메세지값 : 메세지의 내용. payload
- 기타필드
- topic
- partition ID
- offset
- timestamp
- size
- CRC: 순환중복검사. 데이터 무결성 보장
일괄처리(Batching)
- 여러 메세지를 한번의 네트워크요청으로 전송하여 네트워크 왕복비용 절감
- 여러 메세지를 한번에 로그에 기록 → 더 큰 규모의 순차쓰기 연산 → 더 큰 규모의 디스크 캐싱 → 더 높은 디스크 대역폭 달성 단 높은 대역폭과 낮은 응답지연은 trade-off임을 명심해야함
생산자 측 작업흐름
생산자 → 메세지 라우팅계층 → 파티션 도달 → 사본 복사
- 위 설계는 라우팅계층이 하나 더 있어 latency 늘어남
- 일괄처리가 안됨 생산자 → 내부 버퍼에 저장하여 일괄로 라우팅 → 파티션도달 → 사본복사
- 라우팅계층없어 latency줄어듬
- 버퍼에저장해서 일괄전송 가능
소비자 측 작업 흐름
소비자는 특정 파티션의 오프셋을 주고 해당 위치에서부터 이벤트를 묶어 가져옴.
- 푸시모델 : 브로커가 소비자에게 데이터 보냄
- 낮은지연.
- 소비자 데이터 처리속도 늦어지면 소비자 부하 걸릴 위험
- 소비자는 항상 생산자에게 맞춰서 자원 준비해아함
- 풀 모델 : 소비자가 브록커에서 데이터 가져옴
- 메세지 소비속도를 소비자가 결정
- 소비 속도가 느리면 소비자 늘리면됨
- 일괄처리에 적합
- 데이터 없어도 계속 땡겨야해서 소비자 자원많이쓸수있음 → 롱폴링 지원 결론: pull 모델이 좀더 적당
소비자 재조정
어떤 소비자가 어떤 파티션을 책임 질지 다시 정하는 프로세스 코디네이터의 역할이 중요
- 소비자들의 heartbeat를 체크
- 파티션 내 오프셋 정보를 관리 소비자가 합류 / 소비자 탈퇴 / 소비자 장애 등 상황에서 파티션을 소비자에게 재배치한다
상태저장소
- 소비자에대한 파티션의 배치관계 저장
- 소비자그룹의 마지막 오프셋 저장
- 읽기/쓰기 빈번하지만 양은 적음
- 데이터갱신 빈번.
- 데이터삭제 없음
- 데이터 일관성이 중요 따라서, zookeeper같은 key-value저장소 추천.
메타데이터 저장소
토픽설정, 속성정보 저장. (파티션 수, 메세지 보관기간, 사본배치 정보…)
- 자주변경 X
- 양 적음
- 높은일관성 따라서 zookeeper같은 key-value 저장소 추천
복제
- 장애시 데이터가 손상되는 상황을 막기 위해, 복제(replication)활용
- 사본을 파티션에 어떻게 분산할지 = 사본분산계획
- 리더브로커노드가 사본분산계획을 만들어 메타데이터저장소에 보관.
사본 동기화
한 노드의 장애로 메세지가 소실되는것을 막기위해 메세지를 여러 파티션에 두고, 파티션은 여러 사본으로 복제.
해당 사본들을 어떻게 동기화시킬것인가? ⇒ ISR(In-Sync Replicas)개념 사본.
replica.lag.messages (=n)값을 지정하면, 리더 사이 메세지 차이가 n개 이내면 ISR으로 판단
합의 오프셋(committed offset)에 도달한 녀석들만 ISR로 판단하여, 동기화 정도를 판단
- ACK=all
- 모든 ISR이 메세지 수신한걸 확정하고 ACK 응답 받음
- 가장느린 ISR의 응답까지 기다려야하므로 latency 증가\
- 단 메세지의 영속성 측면에서는 좋음
- ACK=1
- 리더만 저장하면 ACK응답
- 응답지연 개선됨
- 사본에 복사하지못하고 리더장애나면 소실가능성있음
- 낮은지연 시스템에 적합
- ACK=0
- 아예 수신확인ACK 기다리지않고 재시도도 안함
- 메세지손실 감수.
- 가장 낮은 latency.
- metric수집이나 logging에 사용
보통 소비자측면에서 볼때 가장 쉬운게 리더에서만 읽는방법. 다만 조건에 따라서 소비자와 가장 가까운 ISR에서 읽어가는 방법을 채택할수도있음.
규모 확장성
- 생산자 : 새로운 생산자 추가/삭제
- 소비자 : 새로운 소비자 추가/삭제. 재조정 매커니즘 필요.
- 브로커 : 결함 내성을 위해 일시적 사본 수를 늘릴수있음.
- 파티션 : 재조정시, 소비자 재조정 필요.
메세지 전달 방식
- 최대 한 번 : ACK=0. 실패해도 재전송X. 소량 데이터 손실 감수 가능할때만 사용
- 최소 한 번: ACK=1 or ACK=all. 메세지 전달되었는지 확인. 실패시 재시도. 단, 중복위험있음
- 정확히 한 번: 구현 어려움. 성능 / 복잡도 희생. 금융관련에서는 이 방식이 적합함.
고급기능
- 메세지 필터링 : 태그 사용.
- 메세지 지연 및 예약전송 : 브로커 임시저장소 사용. 혹은 예약전송 기능 사용.
4. 마무리
추가적으로 논의하면 좋을내용
- 프로토콜
- 메세지 소비 재시도 : 실패시 재시도 전용 토픽 사용해 재시도
- 이력 데이터 아카이브 : 오래된데이터는 HDFS등에 옮기거나 s3등에 옮김.