책/가상 면접 사례로 배우는 대규모 시스템 설계 기초 정리

단일 메시지 브로커 설계하기 - (2) / 마이크로 서비스의 프로세스 간 통신 정리

개발하는 민우 2024. 1. 20. 01:13

단일 메시지 브로커 설계

메시지 순서 유지

  1. 메시지는 발생 순서에 맞게 서비스에 도착해야 함
  2. 중복 처리 되지 않아야 함

  • 스케일 아웃(서비스 인스턴스 3개)가 동일한 메시지 채널을 구독
  • 송신자가 주문 생성, 주문 수정, 주문 취소를 순서대로 게시
  • 네트워크 지연, 가비지 컬렉션 등 여러가지 사유로 인해 → 순서대로 처리되지 않을 수 있음

 

샤드 채널(Sharded channel, partitioned Channel)

  • 하나의 샤드 채널은 2개 이상의 샤드로 구성
  • 송신자(sender)는 메시지 헤더에 임의의 문자열, 바이트 시퀀스를 사용한 샤드 키 명시
  • 메시지 브로커는 이 샤드 키 → 특정 샤드, 파티션에 할당

Kafka와 Zookeeper의 관계

  • Kafka는 소비자 그룹(복수의 수신자 인스턴스를 하나로 묶어 동일한 수신자로 취급) 하여, 하나의 수신자에게 할당
  • 특정 주문 이벤트 → 동일한 샤드에 게시되고 동일한 메시지 소비자에 의해 처리

중복 메시지 처리

문제점

클라이언트가 메시지를 처리하고 데이터베이스를 업데이트하였으나, 수신 확인 신호를 보내기 전 문제 발생 → 메시지 브로커는 수신확인 받지 못하여, 동일한 메시지 다시 전달

멱등성을 가지는 메시지 핸들러

  • 연산을 여러번 적용해도 결과가 달라지지 않는 성질: 멱등성
  • 메시지 핸들러가 멱등성을 가지면 → 시스템 장애로 인한 이벤트 중복이 발생하더라도 결과적으로 동일 상태가 된다

메시지 추적하여 중복 메시지 제거

 

이미 처리된 메시지 아이디를 데이터 베이스에 저장 → DB와 비교하여 신규 메시지인 경우 처리, 중복 메시지인 경우 제거해야 함.

 


트랙잭셔널(Transactional) 메시징

  • 서비스는 데이터베이스 업데이트와 메시지 전송이 하나의 트랜잭션 안에서 원자적으로 실행되어야 함 → otherwise, DB가 저장되지 않고 메시지 전달, DB 저장되었지만 메시지 전달 X 에러 발생

트랜잭션 아웃박스 패턴

  • 메시지 전송 주문 서비스가 주문 객체를 생성, 수정, 삭제함 동시에 OUTBOX라는 데이터베이스 테이블에 저장함
  • 메시지 중계기가 아웃박스 테이블에서 메시지를 읽어 메시지 브로커로 전달, 객체 수정, 메시지 저장은 RDBMS(ACID 트랜잭션)이기 때문에 원자성 보장

폴링 퍼블리셔 패턴

  • RDBMS 사용시, 아웃박스 테이블에 삽입된 메시지를 게시하는 가장 간단한 방법
  • 메시지 중계기 → 아웃박스 테이블을 주기적으로 폴링하여 게시되지 않은 메시지 찾은 후 → 메시지 브로커에 게시, 아웃박스 테이블에서 삭제
  • NoSQL에선 사용 불가

트랙잭션 로그 테일링 패턴을 적용하여 이벤트 게시하기

 

  • 데이터베이스 트랙잭션 로그를 추적(트랜잭션 로그 수집기)가 트랜잭션 로그를 읽어서 모든 변화를 메시지 브로커에 메시지 게시
  • 가장 많이 쓰는 패턴

비동기 메시징을 통한 가용성

  • 동기식 통신 → 애플리케이션의 가용성을 감소 시킴
  • 동기식 통신보다 비동기 메시징 이용 지향

데이터 복제하기

  • 서비스는 요청을 처리하기 위한 데이터 복제본을 만들고 → 복제본을 최신 상태로 유지
  • 데이터의 양이 과도하게 커지면 비효율, 원본 데이터 수정시 해결 X

응답 우선 회신 후 잔여 작업 처리

  • 클라이언트의 요청을 받아서 → 먼저 로컬에서 사용 가능한 데이터로 요청 처리, 그 결과 응답 클라이언트에게 회신 → 비동기 방식으로 다른 서비스에게 메시지 전송하여 후속 프로세스 진행

예) 주문 서비스 → 주문 생성 요청 처리할 때, 최소한 정보로 검증 → 그 후 다른 서비스에게 요청하여 주문 생성 요청 검증

→ 연관 서비스 다운 되더라도 운영 가능 (큐에 처리 과정을 쌓아서 오류 복구후 순차 실행)

→ 주문 검증에 대해 최소한의 보장만 하므로, 정상 생성위해 주기적 폴링 해야 함.

 


헤더/바디에 어떤 내용?

 

  • 메시지의 비지니스 로직: Sender의 포트를 통해 Sender 호출 → 채널에 메시지 배포, 메시지 핸들러는 메시지 채널에서 메시지 읽어와 수신 포트를 통해 전달.

헤더: 본문 데이터를 설명하는 명칭:값 형식으로 된 메타 데이터 집합, 메시지 아이디와 응답을 수신할 메시지 채널

본문: 텍스트, 바이너리 포맷

메시지

  • 문서 - 데이터만 포함된 메시지로, 커맨드에 대한 응답으로 회신되는 메시지
  • 커맨드 - 호출할 오퍼레이션, 매개변수 명시
  • 이벤트: 송신자에게 유의미한 상태 변화 발생했다는 것을 알려주는 메시지 (주문, 고객 같은 도메인 객체의 상태 변경이 발생했다는것을 알려줌)

사용자 멘션은 어떻게?

  • 점대점 방식(Point-to-Point) 방식을 사용함.

단방향 알림

  • 클라이언트가 점대점 채널로 커맨드 메시지 보내면 → 해당 채널 소유하는 서비스가 구독해서 처리하는 방식. 단방향이므로 메시지에 대한 응답 X

첨부파일, 바디에 어떻게 들어가야 할지?

  • 클라이언트가 점대점 채널로 메시지 보내면 → 해당 채널 소유하는 서비스가 구독해서 처리하는 방식. 단방향이므로 메시지에 대한 응답 X
  • 헤더: 단순 메시지 id, 수신 메시지 채널
  • 바디: Content-Type: multipart/form-data로 하면 되지 않을까?