3. 프로세스 간 통신 (IPC)

Bonjugi·2022년 5월 1일
0

아래 3.1, 3.2 장은 익숙한 내용이 많아서 생략한다.

  • 3.1 마이크로서비스 아키텍처 IPC 개요
    • 3.1.1 상호 작용 스타일
    • 3.1.2 마이크로서비스 API 정의
    • 3.1.3 API 발전시키기
    • 3.1.4 메시지 포맷
  • 3.2 동기 RPI 패턴 응용 통신
    • 3.2.1 REST
    • 3.2.2 gPRC
    • 3.2.3 서킷프레이커
    • 3.2.4 서비스디스커버리

출처 :


3.3 비동기 메시징 패턴 응용 통신

3.3.1 메시지 개요

메시지는 헤더와 본문으로 나뉘어짐.
헤더에는 뭐가들어가지? :

  • 메타데이터에 해당하는 키/값
  • 송신자 또는 메시징 인프라에서 생성된 메시지 ID
  • 응답이 출력될 메시지 채널을 가리키는 반환 주소(옵션)

headless 도 있다. payload header 를 활용해야 한다.

메시지의 종류

  • 커맨드 : RPC 요청과 동등함. 작업 + 매개변수
  • 이벤트 : 도메인객체의 상태 변화. (도메인이벤트)

채널의 종류

  • 점대점 (1:1) :

    • point-to-point
    • 주로 커맨드 메시지를 다룸
  • 발행/구독 (1:N) :

    • publish-subscribe
    • 주로 이벤트 메시지를 다룸

3.3.2 메시징 상호 작용 스타일 구현

점대점을 이용한 요청/응답

점대점 채널을 요청채널 + 응답채널 로 만들어서 요청/응답 처리를 구현할수 있다.

요청시에는 MessageId: msgId 를 전달하고, 응답시에 CorrelationId: msgId 로 응답하면서 쌍을 맞췄다.
또한 요청시에 ReturnAddress 로 어디로 보낼지도 적혀있다.

메시징은 기본적으로 비동기 이고, 만약 동기식으로 만들려면 블락을 걸면 된다.

이런 설명이 있긴 한데.. 굳이 메시징을 동기적으로 할일은 없을듯 하다.
메시징 시스템으로도 충분히 모든 상호작용 패턴을 구현할수 있음을 알려주기 위한 의도로 보인다.

발행/구독 을 이용한 요청/응답

점대점과 다를바 없이 응답 채널을 활용하면 된다.
ReturnAddress 가 없고 CorrelationId 만 추가하면 관심있는 컨슈머가 읽어가면 된다.
응답 채널은 점대점을 써도 되고 발행/구독을 활용해도 된다.

3.3.3 메시징 기반 서비스의 API 명세 작성

REST, 오픈API 와 달리 표준이 없으므로 자유롭게 기술하면 된다.

비동기 작업 방식과 발행 이벤트 방식 2가지를 그림으로 볼수있다.

채널명을 커맨드 채널 이벤트 채널 이라고 명시했는데 괜찮은것 같다.

발행 이벤트 문서화

요청/비동기 응답 스타일 API

  • 커맨드 메시지 채널
  • 커맨드 메시지의 타입과 포맷
  • 서비스가 반환하는 응답 메시지의 타입과 포맷

단방향 알림 스타일 API

  • 이벤트 채널
  • 이벤트 메시지 타입과 포맷
  • 커넥션 정보

3.3.4 메시지 브로커

브로커리스 메시징 아키텍처도 있지만 일반적으로 브로커 기반 이 낫다.
(sqs 도 브로커 기반)

브로커리스 기반

안쓰지만 대략 특징만 짚어보자.

장점

  • 직접 전달되므로 네트워크 홉이 1개 더 적음
  • 브로커 장애에 의한 병목점 또는 spof 될일이 없음
  • 별도 설정이 없어도 되어 운영 복잡도 낮음

단점

  • 서비스가 서로의 위치를 알아야 함. (서비스 디스커버리 메커니즘같은게 필요할수도)
  • 메시지 교환시 송/수신자 모두 실행중이어야 함
  • 전달 보장 메커니즘 구현이 더 어려움

브로커 기반 메시징 개요

sqs, kafka, kinesis, rabbitmq 등 메시지가 지나가는 중간지점이 있으면 모두가 해당한다.
어떤 메시지 브로커를 쓸지 결정할때 검토 사항

  • 다양한 프로그래밍 지원여부
  • 메시징 AMQP, STOMP 등의 표준 프로토콜 지원 여부
  • 메시지 순서 보장
  • 전달 보장
  • 영속화 : 브로커가 고장나도 문제 없도록 디스크에 쓰는지?
  • 내구성 : 컨슈머가 브로커에 재접속시, 중단된 시간에 전달된 메시지를 받을수 있는지? (당연한거아닌가?)
  • 확장성
  • 지연시간
  • 경쟁사 컨슈머 지원여부

장단점이 있을수밖에 없다.
속도가 빠르면 메모리에만 쓰면서 내구성을 보장하지 않는다던지, 순서보장이 안된다던지..
하지만 순서 유지확장성 은 필수 이다.

카프카 또는 키네시스 쓰자

채널을 부르는 명칭은 브로커 마다 다르다.
점대점 채널펍섭 채널 을 구분해서 명칭이 있는 브로커들도 있지만 내게 익숙한 브로커들은 딱히 다르지 않다.

  • 카프카 : 토픽
  • 키네시스 : 스트림
  • SQS : 큐 (펍섭 지원 X)

브로커 기반 메시징의 장단점

장점 :

  • 느슨한 결함
  • 메시지 버퍼링 (http 동기식이 아녀서 둘다 실행중이 필요가 없다)
  • 유연한 통신 (3.1.1 절에서 설명한 상호작용 스타일
    - 일대일 (요청/응답, 단방향 알림)
    • 일대다
    • 동기
    • 비동기

단점 :

  • 성능 병목 가능성
  • 단일 장애점 가능성
  • 운영 복잡도 부가

카프카를 쓰면 위 단점 극복이 가능

3.3.5 수신자 경합과 메시지 순서 유지

채널이 샤딩 가능한 브로커 쓰자

  • 카프카 : 파티션
  • 키네시스 : 샤드

3.3.6 중복 메시지 처리

특성상 1번만 전달하는것은 불가능하다.
컨슈머가 처리를 하는중 ack 보내기 직전에 죽는다던지, 네트워크 실패라던지 등의 이슈로 브로커가 재전송 하기도 한다.

카프카의 경우 오프셋으로 폴링해오는 구조라서 재전송은 아니더라도 ack 보내기 전에 죽는것에 대해서 2번 폴링하는 경우는 막을수 없을것 같다.

적어도 한 번 전달 을 보장 해주는 브로커를 쓰자. (sqs 도 보장해준다)

컨슈머가 멱등하게 처리해주자.

이미 취소된 오더를 다시 취소되도 문제없게 설계하면 된다.
하지만 이렇게 멱등한 로직은 잘 없는편이다.

또는 DB 테이블로 1번만 실행되게 하자.

중복처리 하는 테이블에 pk 를 두어 저장하여 한번만 실행되도록 보장하게 한다.
특정 목적으로만 만들어졌기에 dedicated table 이라고도 불리며, 중복처리를 하는 테이블이기에 once table 이라고도 불린다.

3.3.7 트랜잭셔널 메시징

서비스의 트랜잭션만 성공하고 이벤트발행을 실패하면 아주 불안정안 상태가 발생한다.
두 작업이 원자적으로 실행되어야 한다.

DB 테이블을 메시지 큐로 활용

트랜잭셔널 아웃박스 패턴 을 쓴다.
테이블을 임시큐로 쓰는 방법이다.

  1. 도메인 로직 트랜잭션 처리
  2. OUTBOX 테이블 에 메시지 저장
  3. 스케줄러 등을 이용하여 발행한다.

이벤트 발행: 폴링 발행기 패턴 (polling publisher pattern)

OUTBOX 에 저장된 메시지를 주기적으로 폴링해서 발행후 삭제한다.
RDBMS 를 쓰고 있고 규모가 작을 경우 쓸수 있는 가장 간편한 방법.
DB 폴링에는 비용이 유발된다.

이벤트 발행: 트랜잭션 로그 테일링 패턴 (transaction log tailing pattern)

DB 트랜잭션 로그를 테일링 하는 방법.
transaction log miner 로 트랜잭션 로그를 읽어 변경분을 메시지로 브로커에 발행하는것

  • 디비지움 (Debezium) : DB 변경분을 아파치 카프카 메시지 브로커에 발행하는 오픈소스
  • 링크드인 데이터버스 : 오라클 트랜잭션 로그를 마이닝 하여 발행
  • DynamoDB 스트림즈 : 다이나모디비 에티블의 변경분을 발행
  • 이벤추에이트 트램 : MySQL 빈로그 프로토콜, Postgres WAL, 폴링으로 OUTBOX 테이블 변경분을 읽어 카프카로 발행

3.3.8 메시징 라이브러리/프레임워크

3.4 비동기 메시징으로 가용성 개선

3.4.1 동기 통신으로 인한 가용성 저하

REST 는 동기식이라는 치명적인 단점 존재.

레이턴시가 x2 되는것을 차치하고서라도 가용률에 영향을 끼친다.

서비스가 각각 99.5% 라면, 3제곱시 98.5%

3.4.2 동기 상호 작용 제거

다음처럼 이벤트들을 이용하면 어느쪽도 응답을 대기하지 않고 블로킹되지 않는다.
(요청채널과 응답채널을 만드는 방법)

비동기 통신으로 바꾸면 가용률에 영향을 끼치지 않는다.
통신하는 2개의 서비스가 모두 살아있을 필요도 없다.

데이터 복제

만약 외부로 의존하는 API 가 있다면 복제해서 레플리카를 두어 자기 완비형 으로 만들수도 있다.

다만 대용량 데이터의 레플리카를 만드는 것은 대단히 비효율적이다.
가령 소비자 서비스에 있는 엄청난 양의 소비자 데이터를 주문 서비스에 그대로 복제하는것은 실용적이지 않다.

응답 반환 후 마무리

요청 처리 도중 동기 통신을 제거하려면 다음과 같은 흐름으로 처리하면 된다.

3.5 마치며

  • IPC 방식은 여러가지가 있다.
  • 사용성을 높이려면 RPC (rest 나 grpc), 가용성을 높이려면 메시징.
  • 동기식 rpc 라면 서킷브레이커를 두자
  • 메시징의 관건은 DB 를 원자적으로 업데이트하고 발행하자.
    - 트랜잭셔널 아웃박스 패턴 + (폴링 발행기 or 트랜잭션 로그 테일링)

0개의 댓글