서로 다른 시스템 간 비동기로 연동할 때 주로 메시징 시스템을 쓴다.

갑이 을에 데이터 전달하고 싶다. 갑은 전달할 데이터를 가진 메시지 생성해서 메시징 시스템에 전송한다. 메시징 시스템은 메시지를 다시 을에게 전달하고, 을은 전달받은걸로 필요한 작업 처리한다.

시스템 구조가 복잡해 졌지만 다른 이점을 얻을수있다.

  • 두 시스템이 서로 영향을 주고 받지 않는다.
    • 갑이든 을이든 성능저하 발생해도 다른 시스템은 성능저하가 없다.
    • 메시징 시스템은 일단 갑이 보낸걸 저장하고 을의 상황에 성능에 맞게 메시지를 전달한다.
    • 즉, 버퍼역할임
    • 갑의 트래픽이 증가해 메시지 증가해도 을은 자신의 용량에 맞게 메시지를 처리할 수 있다.
  • 확장이 용이하다.
    • 갑이 이제 을 뿐만아니라 병에게도 데이터를 전송해야한다. 갑이 병에게 직접 데이터를 전송했다면 갑에 새로운 코드를 추가해야했을것이다. 그러나 메시징 시스템을 쓰면 시스템 병을 메시징 시스템에 연결만 하면된다. 갑의 코드를 수정할 필요가 없는것.

메시징 시스템으로 많이 쓰이는 기술은 카프카, 레빗MQ, 레디스 pub/sub 등이 있다.

카프카를 고를때 고려할 만한 특징

  • 높은 처리량 자랑. 초당 백만개 이상의 메시지 처리 가능
  • 수평 확장 용이. 서버(브로커), 파티션, 소비자 늘리면 됨
  • 카프카는 메시지를 파일에 보관하여 메시지 유실 안됨
  • 1개의 토픽이 여러 파티션 가질 수 있는데 파티션 단위로 순서를 보장. 하지만 토픽 수준에서는 순서 보장 못함
  • 소비자는 메시지를 언제든 재처리 가능
  • 풀(pull) 모델을 씀. 소비자가 카프카 브로커에서 메시지를 읽어 가는 방식

레빗MQ 주요 특징

  • 클러스터를 통해 처리량 높이기 가능. 단, 카프카보다 많은 자원 필요
  • 메모리에만 메시지 보관하는 큐 설정을 사용하면 장애 상황 시 메시지 유실될 수 있다
  • 메시지는 큐에 등록된 순서대로 소비자에게 전송
  • 메시지가 소비자에게 전달되었는지 확인 하는 기능 제공
  • 푸시(push)모델 사용. 래빗MQ 브로커가 소비자에게 메시지 전송. 소비자 성능이 느려지면 큐에 과부하 걸려 전반적으로 성능 저하될 수 있다.
  • 다재 다능. AMQP, STOMP 등 여려 프로토콜 지원. 게시/구독, 요청/응답, 점대점 패턴 지원함. 또한 우선순위 지정해서 처리 순서 변경도 가능

레디스 pub/sub 주요 특징

  • 메모리 써서 지연 시간 짧고, 래빗MQ대비 처리량 높음
  • 구독자 없으면 메시지 유실
  • 기본적으로 영구 메시지 지원 안함
  • 모델이 단순해 사용하기 쉽다.

메시지 생성 측 고려 사항

생성 시 고려할 건은 메시지 유실에 대한 것. 예를 들어 메시지 전송 과정에서 타임아웃이 발생할 수 있다. 타임아웃 문제는 생산자와 메시징 시스템 간의 네트워크 연결이 불안정하면 언제든 발생할 수 있다. 이때 오류 처리를 위해 할 수 있는 방법은 3가지가 있다.

  • 무시한다.
  • 재시도한다.
  • 실패 로그 남긴다. 재시도할때 중복된 메시지가 전송될수 있다. 실제로는 전송 성공했는데 일시적인 네트워크 오류로 전송에 실패한 것으로 인지하고 재시도할 수 있기 때문. 메시징 시스템에 중복 수신을 방지하는 기능을 제공하지 않으면 메시지 소비자가 중복 메시지를 알맞게 처리해야 한다.

글로벌 트랜잭션과 메시지 연동

여러 DB를 하나의 트랜잭션으로 묶어 처리 가능하다. 글로벌 트랜잭션이 이에 해당한다.

글로벌 트랜잭션을 사용하면 여러 자원(여러 DB)에 대한 변경을 한 트랜잭션으로 묶어 처리 가능하다. 예를 들어 A DB는 성공했는데 B DB처리 시 오류가 발생하면 A, B 둘다 모두 롤백 가능하다. 글로벌 트랜잭션을 구현하는 알고리즘으로 2단계 커밋(2-Phase Commit)을 사용하는데, 그래서 글로벌 트랜잭션을 2PC라고 표현하기도 한다.

메시지 소비 측 고려 사항

소비자는 다음 2가지 이유로 동일 메시지를 중복해서 처리 가능하다.

  • 메시지 생산자가 같은 데이터를 가진 메시지를 메시징 시스템에 두 번 전송
  • 소비자가 메시지를 처리하는 과정에서 오류가 발생해서 메시지 재수신

수신자 입장에서 동일 데이터 가진 중복 메시지 처리하는 방법은 메시지에 고유한 ID 부여해서 처리 여부를 추적하는것.

메시지 재수신이 가능한 경우 소비자가 메시지를 처리하는 과정에서 오류가 발생하면 재처리를 위해 메시지를 다시 수신할 수 있다. 예를 들어 메시지 처리를 위해 외부 API 호출했는데 읽기 타임아웃이 발생할수도있다. 이때 소비자는 메시지 처리에 실패했다 생각하고 메시징 시스템으로 부터 같은 메시지를 다시 수신하여 재시도 할 수 있다. 하지만 외부 API를 호출했을때 읽기 타임아웃이 발생했으면 이미 성공했을 가능성이 있다. 실제 성공했다면 수신자는 외부 API를 중복해서 2번 호출하는셈.

메시지 재수신에 따른 중복 처리를 대응하는 방법은 4장에서 언급한 멱등성을 갖도록 API를 구현하는것. API가 멱등성을 가지면 요청을 여러번해도 결과가 안바뀐다.

메시지 종류: 이벤트, 커맨드

  • 이벤트
    • 주문함
    • 로그인 실패함
    • 상품 정보 조회함
    • 배송 완료함
  • 커맨드
    • 포인트지급하기
    • 로그인 차단하기
    • 배송 완료 문자 발송하기 이벤트는 상태 변경과 관련있고 커맨드는 무언가를 요청하는 메시지.

이벤트 메시지는 소비자 확장에 적합함. 커맨드는 할 줄 아는 걸 시켜야되니 이걸 할 줄 아는 서비스에만 한정된 메시지가 되는것임.

궁극적 일관성(eventual consistency)

비동기 연동 설명시 자주등장하는 용어. 최종적 일관성, 결과적 일관성이라고도 불림. 궁극적 일관성은 일관성 모델 중 하나. 주로 분산 시스템에서 데이터 복제 다룰 때 사용. 이 모델은 두 데이터 저장소 간의 일관성 보장하긴 하지만 즉시가 아닌 일정 시간이 지난 후 일관성이 맞춰진단 특징이 있다. 즉, 일시적 불인치는 발생할 수 있다는 뜻.

비동기 메시징 방식도 이와 유사항 특성 가진다.배송 서비스에서 배송 상태를 완료로 하더라도 해당 변경 내용이 메시지 통해 주문 서비스로 전달되기 전까지는 두 시스템감 서로 불일치 할수있다.