🖥️ CS/Architecture

[마이크로서비스 아키텍처 구축] 4. MSA 마이크로서비스 통신 방식

한국의 메타몽 2024. 10. 22. 01:31

book

해당 글은 마이크로서비스 아키텍처 구축에서 학습한 내용을 다룹니다.



목차

  1. 프로세스 내부에서 프로세스 사이로
    (1) 프로그램, 프로세스, 스레드의 관계
    (2) 왜 프로세스 간 호출의 오버헤드가 더 클까?
    (3) 인터페이스 변경
    (4) 에러 처리
  2. MSA 통신 방식
  3. 동기 대 비동기
  4. 이벤트 기반 통신


1. 프로세스 내부에서 프로세스 사이로

(1) 프로그램, 프로세스, 스레드의 관계

1. 프로그램이 구동되면 메모리에 올라간다.
2. 메모리에 올라간 프로그램의 상태를 프로세스라고 한다.
3. 이때 프로세스 내에서 실행되는 여러 흐름의 단위가 '스레드'이다.

Monolithic에선 프로세스 내부에서 호출이 이루어졌다면 MSA에선 프로세스 사이로 네트워크릍 통해 통신이 이루어짐

  • 프로세스 내부 호출이 일어날 때, 런타임은 호출의 영향을 줄이려고 마치 처음에 호출이 없었던 것처럼 호스트 최적화를 수행할 수 있음
  • 하지만 프로세스 간 호출에서는 이러한 최적화가 불가능하며, 패킷을 전송해야 함
  • 프로세스 내 호출의 오버헤드 < 프로세스 간 호출의 오버헤드
    • ex ) 데이터 센터 안에서 단일 패킷이 왕복하는데 걸리는 시간은 밀리초 단위로 측정 가능한 반면, 메서드 호출의 오버헤드는 걱정하지 않아도 될 만큼 작음

(2) 왜 프로세스 간 호출의 오버헤드가 더 클까?

  • 메서드에 매개변수를 전달할 때, 보통 전달할 데이터의 구조체는 이동하지 않음
    • 대신 메모리 위치에 대한 포인터를 전달
    • 때문에 데이터 복사가 이루어지진 않아 더 많은 메모리가 할당되진 않음
  • 반면 네트워크를 통한 MSA 호출이 이루질 때, 데이터는 실제 네트워크를 통해 전송될 수 있도록 직렬화가 이루어짐
    • 수신처에서는 반대로 역직렬화가 이루어짐

(3) 인터페이스 변경

  • Monolithic에서는 인터페이스가 변경될 경우 보통 사용중인 IDE를 통해 쉽게 유관 코드의 리펙터링이 가능
  • 그러나 MSA에서는 별개의 프로젝트로 나뉘므로, 호출처와 수신처의 인터페이스가 동시에 변경되는 것을 보장하려면 락스텝 배포 방식이 권장됨
    • 락스텝 배포 : 복수 개의 서비스를 동일한 CI/CD로 배포하는 것

(4) 에러 처리

  • 분산환경에서는 기존 Monolithic에서 예상 못한 에러들을 많이 마주함
    • ex ) 네트워크 시간 초과, 다운스트림 MSA가 일시적으로 사용 못하는 상황, 네트워크 연결이 끊김, 과도한 메모리 사용으로 컨테이너가 다운됨
  • 일반적으로 HTTP 프로토콜을 통해 MSA간의 호출이 이루어지는데, 보통 에러 코드의 의미는 하기와 같음
    • 400번대 : 본질적으로 요청 에러이며, 다운스트림(수신처)이 요청에 원래부터 문제가 있음을 클라이언트에게 알려줌
      • 이런 경우는 요청을 포기해야함. 404 Not Found에 재시도하는 것은 의미가 없음
    • 500번대 : 다운스트림 서버에 문제가 있으며, 일부 코드는 일시적인 문제라도 클라이언트에게 알려줌
      • ex ) 503 Service Unavailable은 일시적으로 다운스트림 서버가 요청을 처리할 수 없는 상태
  • 이러한 Monolithic에서 예상 못했던 에러처리를 위해 Circuit Breaker Pattern 적용 및 Resilience4j와 같은 라이브러리 사용이 필요됨


2. MSA 통신 방식

  • 동기식 블로킹 : MSA는 다른 MSA를 호출하고 응답을 기다리는 동안 다른 작업을 차단
  • 비동기식 논블로킹 : 호출을 보낸 MSA는 호출 수신 여부에 관계없이 처리를 계속할 수 있음
  • 이벤트 기반 : MSA는 다른 MSA가 소비하고 반응하는 이벤트를 발산. 이벤트를 발행하는 MSA는 자신이 발행하는 이벤트를 소비하는 MSA가 어떤 MSA인지 알지 못함
  • 공통 데이터 : 통신 방식으로 자주 볼 수 없는 MSA인 경우, 일부 공유 데이터 소스를 통해 협업 (= DB를 공유)
    • kafkagRPC Redis 같은 기술을 사용하지 못하는 구형 시스템의 경우, 대안책이 될 수 있음


비동기식 논블록킹과 관련된 예시

  • 환율과 같은 정보는 하루 종일 수시로 변동되며, 일반적으로 메시지 브로커를 통해 환율 정보를 받음
async function f() {
    let eurToGbp = new Promise((resolve, reject) => {
        // 최신 EUR 대 GBP 환율을 가져오는 코드
        ...
    });
    var latestRate = await eurToGbp;
    process(latestRate);
}
/*
1. 최신 EUR 대 GBP 환율을 가져올 때까지 대기
2. Promise가 수행 완료될때까지 실행되지 않음
*/

  • await을 사용해 eurToGbp를 참조할 때는 latestRate의 상태가 채워질 때까지 블로킹됨
  • processeurToGbp 상태를 확인할 때까지 완료되지 않음
  • 환율을 비동기적으로 받더라도 이 문맥에서 awiat을 사용한다는 것은, latestRate의 상태가 해결될 때까지 블로킹 된다는 의미
  • 따라서 환율을 가져오는 데 사용하는 하부 기술을 사실상 비동기식으로 간주할 수 있더라도 (ex : 환율 대기), 코드 관점에서는 본질적으로 동기식 블로킹 상호작용


3. 동기 대 비동기

  • 동기식 블로킹, 비동기식 논블로킹이 있음
  • 메세지 브로커를 통한 비동기식 요청은 비동기식 논블로킹에 해당됨
  • 동기식 블로킹이나 비동기식 논블로킹 모두 호출이 정상적으로 이루지지 않을 경우 retry와 같은 일종의 보상 조치를 취할 수 있음


4. 이벤트 기반 통신

  • MSA가 다른 MSA로 작업을 요청하는 대신, MSA가 다른 MSA로 수신 여부가 보장되지 않은 이벤트를 발생
  • 이벤트 리스너가 자체 실행 스레드에서 실행되기 떄문에 본질적으로 비동기식 상호작용
    • Java에서 ApplicationEventPublisher를 활용한 Event기반 동작이 예시가 됨
    • 또는 Kafka RabbitMQ같은 메시지 큐도 이벤트의 발행 가능
      • JavaApplicationEventPublisher와는 다르게 이벤트트의 저장보존이 가능하다는 장점이 있음
  • 이벤트 기반 협업 방식에서 이벤트 발행자는 다운스트림 마이크로서비스가 무엇을 할 것인지 알 필요가 없으며, 실제로 존재하는지조차 알 수 없음
  • 이는 결과적으로 느슨한 도메인 결합을 유도
  • 추후 SAGA 패턴에서 유사 개념을 다룰 예정

Event vs Message

  • Event : 사실(fact). 이벤트는 브로드캐스트를 통해 통신이 이루어짐
    • 빌셍한 일에 대한 알림이고, 이를 다운스트림에서 수신할지 여부는 불확실할 수 있음
    • ex : 소비자가 주문을 완료했다.
  • Message : 전달 매채(medium). 메시지 브로커와 같은 비동기 통신 메커니즘을 통해 전송됨
    • 특정 작업을 지시하거나 데이터를 전달하기 위함이며, 반드시 처리되어야 함
    • ex : 주문 생성 요청 메시지.

메시지 브로커 vs 이벤트 브로커 차이

  • 메시지 브로커, 이벤트 브로커 모두 메시지 혹은 이벤트 통신을 위해 기술을 선택하기 보다는 MSA아키텍처 상에서, 서버간의 통신 혹은 데이터 동기화를 목적으로 사용하는 경우가 많음
  • 더불어 두 가지 기술 모두 MSA 통신 체계를 구축하기 위한 공통적인 기능을 모두 수행할 수 있음
  • 실제 kafka 사용 시 이벤트라는 말 보다는 메시지를 보낸다는 경우가 더 많음
  • 그럼에도 불구하고 Kafka 가 이벤트 브로커로 분류되는 이유는 메시지 큐를 사용하지 않으며, Producer 의 이벤트 발행을 통해Consumer 에게 메시지를 전송하기 때문
  • 프로그래밍 전반에 사용되는 메시지 아키텍처는 대부분 비동기 통신을 사용하며, 더불어 메시지 브로커의 라우팅 기능이 확립되면서, 이벤트처럼 통신 상대를 명확히 지정할 이유가 사라짐
> 송신자와 수신자 기존 수행할 작업을 메시지 브로커에게 전가 했다고 볼 수 있다. 송신자가 통신을 보내기 위해 수신자를 파악할 이유가 사라진것이다. 
  • 이는 이벤트가 동작을 처리할 때 특정 작업 상대를 지정하지 않는다는 점에서 일정부분 비슷하여, 메시지 통신과 이벤트 통신의 경계가 모호해졌다고 볼 수 있음

Event의 사용 예시

  • 유저가 회원하는 이벤트가 발행되면 해당 이벤트를 구독중이던 다른 서비스(ex : 환영 이메일, 쿠폰 발급)가 구독에 따른 행동을 취함