비동기 기법 - 메시징
- 서로 다른 시스템 간 비동기로 연동할 때 주로 메시징 시스템을 쓴다
- 갑이 을에 데이터 전달하고 싶다
- 갑은 전달할 데이터를 가진 메시지 생성해서 메시징 시스템에 전송한다
- 메시징 시스템은 메시지를 다시 을에게 전달하고, 을은 전달받은걸로 필요한 작업 처리한다
- 시스템 구조가 복잡해 졌지만 다른 이점을 얻을수있다
- 두 시스템이 서로 영향을 주고 받지 않는다.
- 갑이든 을이든 성능저하 발생해도 다른 시스템은 성능저하가 없다
- 메시징 시스템은 일단 갑이 보낸걸 저장하고 을의 상황에 성능에 맞게 메시지를 전달한다.
- 즉, 버퍼역할임
- 갑의 트래픽이 증가해 메시지 증가해도 을은 자신의 용량에 맞게 메시지를 처리할 수 있다.
- 확장이 용이하다.
- 갑이 이제 을 뿐만아니라 병에게도 데이터를 전송해야 할 때
- 갑이 병에게 직접 데이터를 전송했다면 갑에 새로운 코드를 추가해야했을것
- 그러나 메시징 시스템을 쓰면 시스템 병을 메시징 시스템에 연결만 하면된다
- 갑의 코드를 수정할 필요가 없는것.
메시징 시스템으로 많이 쓰이는 기술은 카프카, 레빗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)
- 비동기 연동 설명시 자주등장하는 용어. 최종적 일관성, 결과적 일관성이라고도 불림.
- 궁극적 일관성은 일관성 모델 중 하나.
- 주로 분산 시스템에서 데이터 복제 다룰 때 사용.
- 이 모델은 두 데이터 저장소 간의 일관성 보장하긴 하지만 즉시가 아닌 일정 시간이 지난 후 일관성이 맞춰진단 특징이 있다.
- 즉, 일시적 불일치는 발생할 수 있다는 뜻.
- 비동기 메시징 방식도 이와 유사한 특징 가진다.
- 배송 서비스에서 배송 상태를 완료로 하더라도 해당 변경 내용이 메시지 통해 주문 서비스로 전달되기 전까지는 두 시스템감 서로 불일치 할수있다.