전자책 출간 알림 [마이크로서비스패턴 쉽게 개발하기]

티스토리 뷰

아래 글과 크리스리처드슨의 '마이크로서비스 패턴'을 참고했습니다.

비동기메시징시스템 이해: https://victorydntmd.tistory.com/343

kafka vs RabbitMQ 비교: https://coding-nyan.tistory.com/129

메시지 처리순서 보장

- 메시지 처리순서 문제 예시

  . 주문서비스는 3개의 인스턴스를 가진다.(예: k8s라면 Pod가 3개)

  . 주문생성메시지가 요청채널에 발행된다. 주문서비스인스턴스1이 처리를 시작한다.

  . 주문취소메시지가 요청채널에 발행된다. 주문서비스인스턴스2가 처리를 시작한다.

  . 주문생성메시지가 먼저 도착했으나 주문서비스인스턴스1이 일시적 장애로 처리가 오래 걸린다면 주문취소가 먼저 처리될 수도 있다. 

- 메시지 처리순서 해결안

  . 요청채널을 파티션으로 분할한다. (Sharding한다.)

  . 각 파티션과 처리서비스인스턴스는 1대1로 한다. 즉 파티션 3개에 주문서비스 인스턴스를 1개씩 배정한다.

  . 주문생성메시지와 주문취소메시지를 동일한 파티션에 발행한다. 배당된 주문서비스인스턴스가 처리하므로 메시지 처리순서가 보장된다. 

* 메시지 큐 제품 중 Kafka는 위 해결안을 지원하나, RabbitMQ는 지원하지 않습니다.

 

메시지 중복처리 방지

첫번째 방법은 멱등성(idempotence)있는 메시지 핸들러를 작성하는것이고, 두번째 방법은 메시지를 추적하고 중복을 제거하는 방법입니다. 

쉽게 말해 여러번 실행해도 문제없도록 어플리케이션을 잘 짜거나, 메시지 중복이 안 생기게 하는 방법입니다.  

멱득성 있게 어플리케이션 짜기는 매우 어려우므로 메시지 중복을 제거하는 방법을 사용합니다.

 

메시지 발행자 관점에서 중복 제거 방안

이벤트저장소에 등록된 이벤트(예: 새로운 주문 요청)는 다른 서비스(예: 결제, 음식점, 배달 등)의 수행을 위해 메시지가 발행되어야 합니다. 
그래서 이벤트저장소 처리기는 이벤트저장소에 이벤트를 저장하고 반드시 메시지브로커의 응답채널에 메시지를 발행 합니다. 
그럼 그 응답채널을 구독한 메시지컨슈머(다른 서비스)가 자신의 일을 하게됩니다. 
 
그런데 여기서 메시지브로커의 응답채널에 동일한 메시지가 중복해서 발송될 수 있습니다.

이의 해결책은 메시지브로커가 중복메시지를 제거하면 됩니다. event store가 RDB일때와 NoSQL일때 다릅니다.

  • Event store가 RDB일때
    • 메시지발행 후 메시지발행여부테이블에서 해당 event를 찾아 발행여부flag를 변경합니다. 이 작업은 메시지발행과 같은 transaction으로 하여 Atomicity(원자성-db writing은 All or nothing)성질에 따라 수행을 보장합니다.
    • 메시지브로커는 메시지발행여부 테이블을 보고 중복메시지를 걸러냅니다.
  • Event Store가 NoSQL일때
    • 별도의 테이블이 아닌 event store의 해당 event record의 발행여부flag를 변경합니다.
    • 메시지브로커는 event store의 발행여부 flag를 보고 중복메시지를 걸러냅니다.

 

메시지 컨슈머 관점에서 중복 제거 방안

메시지브로커는 어떤 이유로든 동일한 메시지를 2개 이상 보낼 수 있습니다.

메시지 컨슈머는 중복메시지를 걸러내는 방안이 필요합니다. 메시지컨슈머가 사용하는 DB유형이 RDB일때와 NoSQL일때 다릅니다.

RDB사용시

처리된메시지ID를 관리하는 Table을 만듭니다.  자신의 DB 업데이트와 완료메시지ID 테이블 업데이트를 동일 transaction으로 합니다. RDB의 Atomicity(원자성)에 의해 안전하게 데이터가 관리됩니다.  (ACID에 대한 이해 참조)

메시지컨슈머는 자신의 DB업데이트 전에 완료메시지ID테이블을 검사하여 이미 있는 경우 수행을 중단합니다. 없는 경우는 수행 후 완료메시지ID테이블에서 해당 event를 찾아 상태를 '완료'로 바꿉니다.

 

NoSQL사용 시

NoSQL은 Atomicity를 보장하지 못하므로 별도 table을 만들면 안됩니다. 

메시지 컨슈머는 자신의 DB에 '완료메시지ID'필드를 만듭니다.

그리고 자신의 DB 업데이트 전에 이 '완료메시지ID'필드에 요청된 메시지ID가 있는지 검사합니다. 있으면 수행을 중단하고, 없으면 수행 완료 후 이 필드에 처리한 메시지ID를 추가합니다.

 

완료메시지ID 관리 향상

메시지ID 자체를 계속 추가하는건 자칫 레코드가 너무 많아질 수 있습니다.

메시지ID를 일련번호로 관리하면 Aggregate별로 max값만 관리하면 됩니다.

예를 들어 Aggregate Order의 마지막 처리된 메시지ID가 1000이라면, 메시지브로커가 1000 미만을 보내면 무시하면 됩니다.

 

 

 

댓글

전자책 출간 알림 [마이크로서비스패턴 쉽게 개발하기]