비동기 기법 - 메시징

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

메시징 시스템으로 많이 쓰이는 기술은 카프카, 레빗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번 호출하는셈.
  • 메시지 재수신에 따른 중복 처리를 대응하는 방법은 멱등성을 갖도록 API를 구현하는것.
  • API가 멱등성을 가지면 요청을 여러번해도 결과가 안바뀐다.

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

  • 이벤트
    • 주문함
    • 로그인 실패함
    • 상품 정보 조회함
    • 배송 완료함
  • 커맨드
    • 포인트지급하기
    • 로그인 차단하기
    • 배송 완료 문자 발송하기
  • 이벤트는 상태 변경과 관련있고 커맨드는 무언가를 요청하는 메시지.
  • 이벤트 메시지는 소비자 확장에 적합함.
  • 커맨드는 할 줄 아는 걸 시켜야되니 이걸 할 줄 아는 서비스에만 한정된 메시지가 되는것임.

궁극적 일관성(eventual consistency)

  • 비동기 연동 설명시 자주등장하는 용어. 최종적 일관성, 결과적 일관성이라고도 불림.
  • 궁극적 일관성은 일관성 모델 중 하나.
  • 주로 분산 시스템에서 데이터 복제 다룰 때 사용.
  • 이 모델은 두 데이터 저장소 간의 일관성 보장하긴 하지만 즉시가 아닌 일정 시간이 지난 후 일관성이 맞춰진단 특징이 있다.
  • 즉, 일시적 불일치는 발생할 수 있다는 뜻.
  • 비동기 메시징 방식도 이와 유사한 특징 가진다.
  • 배송 서비스에서 배송 상태를 완료로 하더라도 해당 변경 내용이 메시지 통해 주문 서비스로 전달되기 전까지는 두 시스템감 서로 불일치 할수있다.