API 디자인과 프로세스 간 통신

Numberbeen·2023년 2월 1일
1

DevOps Bootcamp

목록 보기
27/30

API 디자인과 프로세스 간 통신

"마이크로서비스 간의 통신" 이라고 명시하지 않고 "프로세스 간 통신 (Inter-Process communication, IPC)" 라고 이름 붙인 것은 마이크로서비스는 하나의 프로세스 단위로 실행 되기 떄문이다.

이번 챕터에서는 IPC 라는 용어가 대중적으로 쓰이는 용어이므로, "마이크로서비스 간의 통신" 임을 염두하고 공부하자.

프로세스 간의 통신

서비스와 서비스가 통신하기 위해서는 인터페이스가 존재해야 하고, 인터페이스가 요구하는 방식대로 커뮤니케이션 해야 한다.

클라이언트/서버 아키텍처를 통해 HTTP 라는 프로토콜을 배우고, HTTP 위에서 작동하는 메소드와 엔드포인트로 구성된 REST API(Application Programming interface) 를 이용한 통신을 배웠다.

동기/비동기

HTTP 프로토콜은 기본적으로 TCP(또는 UDP) 연결을 만들고, 이 위에다 요청에 따라 즉시 응답이 오는 형태로 구현되어 있다. 즉 동기적 인 응답을 제공한다.

비동기적인 응답으로는 카카오톡을 예로 들자면 발신자는 수신자가 즉시 메시지를 보고 답장하리라 기대하지 않는다. 수신자가 바로 답장할 수 도 있고 나중에 답장할 수 도 있는 상황이다. 이러한 패턴을 비동기적 커뮤니케이션 이라고도 한다.

앞서 예를 든 💡 HTTP 는 비동기가 아닌가?

HTTP는 컴퓨터와 컴퓨터 사이의 네트워크 통신이고, 이는 네트워크 지연에 따라 즉시 응답이 오지 않을 수도 있기 때문에 이를 비동기로 인식할 수 있다. 이는 프로그래밍 언어 관점에서 틀린 말은 아니다.

하지만 프로세스 간 통신의 관점에서 HTTP 는 동기적인 메커니즘으로 분류한다. 왜냐? 어쨋든 서로 통신하는 두 컴퓨터는 모두 켜져있어야 하며, 클라이언트는 서버가 제 때 응답을 줄것이라 기대하기 때문이다.

일대일 및 일대다 통신

시간의 관점이 아닌 다른 관점에서도 살펴보자. 1:1 로 작동하지 않는 커뮤니케이션 방식도 세상에는 많이 존재한다. 유튜브 구독 서비스는 1:1 로 커뮤니케이션 하지 않고 구독자 모두에게 동일한 영상과 메시지를 제공하는 형태, 즉 일대다 커뮤니케이션 방식을 취한다.

그러면 💡 HTTP 는 일대일? 일대다?

요청 응답으로 이루어진 한번의 트랜잭션에서 서버는 여러 클라이언트에게 동시에 응답을 전달하지 않는다. 서버가 여러 클라이언트를 상대할 수 는 있지만, 그것은 여러번의 1:1 커뮤니케이션이지, 동시에 여러 클라리언트를 상대하는 1대다 커뮤니케이션이 아니다. 따라서 HTTP 통신은 일대일 통신 이라고 볼 수 있다.

프로세스 간의 직접/간접 연결

클라이언트와 서버 아키텍처를 통한 두 프로세스 간의 직접 연결 방식도 있지만 클라이언트와 서버 중간에 메시지 브로커 가 두 프로세스 사이에 위치해서 오고가는 메시지 자체를 관리하는 형태의 연결 방식도 있다. 이 경우 비동기적인 처리는 물론 둘 중 하나의 프로세스사 실행중이 아니더라도 서로 메시지를 주고 받을 수 있다.


대표적인 데이터 교환 포맷 JSON

Json의 탄생 배경

JSON 은 JavaScript Object Notation 의 줄임말로, 데이터 교환을 위해 만들어진 객체 형태의 포맷이다.
네트워크를 통해, 어떤 객체 내용을 다른 프로그램에게 전송한다고 가정한다. 이 객체 내용을 일종의 메신저 혹은 채팅 프로그램에서 쓰는 하나의 메시지라고 한다면, 다음 객체를 어떻게 전송할 수 있을까?

const message = {
  sender: "넘버빈",
  receiver: "빈버넘",
  message: "버넘아 오늘 저녁 뛰러 갈래",
  createdAt: "2023-02-01 13:10:10"
}
[코드] 메시지를 담고 있는 객체 message

메시지 객체가 전송 가능하려면, 메시지를 보내는 발신자와 메시지를 받는 수신자가 같은 프로그램을 사용하거나, 문자열처럼 범용적으로 읽을 수 있는 형태여야 한다.

전송 가능한 조건

  • 수신자와 발신자가 같은 프로그램을 사용한다.
  • 또는, 문자열처럼 범용적으로 읽을 수 있어야 한다.

객체 타입 변환을 이용해 String 로 변환할 경우 객체 내용을 포함하지 않는다. JavaScript 에서 객체에 메소드 (message.toString())나 형변환(String(message))을 시도하면, [object Object] 라는 결과를 리턴한다.

이 문제를 해결하기 위해서는 객체를 JSON의 형태로 변환하거나 JSON을 객체의 형태로 변환하는 방법이다.

  • JSON.stringify : Object type을 JSON으로 변환
  • JSON.parse : JSON을 Object type으로 변환
  • 자세한 내용은 JSON 공식 문서를 참고( 봐도 잘 모르겠다...)
let transferableMessage = JSON.stringify(message)
console.log(transferableMessage)  // `{"sender":"넘버빈","receiver":"빈버넘","message":"버넘아 오늘 저녁 뛰러 갈래","createdAt":"2023-02-01 13:10:10"}`
console.log(typeof(transferableMessage)) // `string`
[코드] message 객체를 JSON으로 변환하는 메소드 JSON.stringify

위 코드처럼 stringify 하는 이 과정을 직렬화(serialize) 한다고 한다.

JSON 으로 변환된 객체의 타입은 문자열이다. 발신자는 객체를 직렬화한 문자열을 누군가에게 객체의 내용을 보낼 수 있다. 그렇다면 수신자는 이 문자열 메시지를 어떻게 다시 객체의 형태로 만들 수 있을까? 바로 JSON.parse 를 사용하면 된다.

let packet = `{"sender":"김코딩","receiver":"박해커","message":"해커야 오늘 저녁 같이 먹을래?","createdAt":"2021-01-12 10:10:10"}`

let obj = JSON.parse(packet)
console.log(obj)
/*
 * {
 * sender: "넘버빈",
 * receiver: "빈버넘",
 * message: "버넘아 오늘 저녁 뛰러 갈래",
 * createdAt: "2023-02-01 13:10:10"
 * }
 */
 console.log(typeof(obj))
 // `object`
[코드] 직렬화된 JSON에 메소드 JSON.parse를 적용하면 다시 객체의 형태로 변환할 수 있습니다.

JSON.parse를 적용하는 이 과정을 역직렬화(deserialize)한다고 한다.

JSON은 서로 다른 프로그램 사이에서 데이터를 교환하기 위한 포맷이며, JSON 포맷은 자바스크립트을 포함한 많은 언어에서범용적으로 사용하는 유명한 포맷이다.

JSON의 기본 규칙

JSON을 얼핏 보기에 자바스크립트의 객체와 별반 다를 바가 없어 보이지만, 자바스크립트의 객체와는 미묘하게 다른 규칙이 있다.

또한 JSON은 키와 값 사이, 그리고 키-값 쌍 사이에는 공백이 있어서는 안된다.


동기식 요청/응답 통신 REST

REST

REST는 HTTP로 소통하는 프로세스 간 통신 규약
REST API는 웹에서 사용되는 데이터나 자원(Resource)을 HTTP URI로 표현하고, HTTP 프로토콜을 통해 요청과 응답을 정의하는 방식이다.

현대에 있어서 HTTP 메시지의 body 부분을 JSON의 형태로 다루는 것이 보통이며, 이 때 HTTP 헤더의 Content-Type의 값(MIME 타입)은 application/json으로 설정한다.

REST의 장점

  • 포스트맨, curl 등의 도구를 사용해 간편하게 테스트가 가능
  • 요청/응답 통신을 직접 지원
  • 시스템 아키텍처가 단순하다.

REST의 단점

  • 요청/응답만 지원
  • 메시지를 주고받기 위해서는 클라이언트와 서버 프로세스가 둘 다 실행 중이어야만 한다.
  • 요청 한 번으로 여러 리소스를 조회하기 어렵다.
  • 메소드만으로는 한번의 요청을 통해 이루어지는 다양한 작업들을 대표하기 어렵다.

메시지 브로커를 이용한 비동기식 통신

Review: 프로세스 간 통신

  • 요청을 보내는 즉시 수신자로부터 응답이 오길 기대하는 "동기적 방법"
    • 클라이언트-서버 아키텍처의 REST(HTTP)가 대표적
  • 요청을 일단 보내놓고 수신자가 받을때까지 보관했다가 처리하는 "비동기적 방법"
    • 수신자가 받기 전에 누군가는 메시지를 보관해놓아야 한다. -> 메시지 브로커 (메시지 큐)

메시지 브로커가 필요한 이유?

분산 애플리케이션에서 프로세스 간의 느슨할 결합을 제공하는 것이 가장 큰 장점이다. 그렇다면 강하게 결합된 시스템의 단점은 무엇인가?

서로 연결되어있는 시스템중 한 곳에서 장애가 발생했을 때 그 장애가 연결된 다른 시스템들에게 영향이 간다.

예를 들어 음과 같은 상황에서, Server 2 에 장애 상황이 발생하면, Server 1 에서 DB2로 가는 데이터가 유실된다.

그러나 중간에 메시지 브로커가 있다면, 한 시스템의 장애가 다른 시스템에 주는 영향을 줄일 수 있다.

Server 2에 일시적인 장애가 발생하더라도, 복구 기간동안 메시지 브로커가 메시지를 보관해놓기 때문에, 데이터가 유실되지 않는다.

주요 용어

  • 메시지를 보내는 사람이 생산자(producer), 메시지를 받는 사람이 소비자(consumer)
  • 메시지를 보내는 것을 발행(publish), 메시지를 받는 것을 소비(consume)

메시지 브로커의 특징

  • 프로그램 간의 직접 연결 없음: less dependency, fault tolerant
    • 소비자 프로세스가 죽어있어도 생산자는 메시지를 보낼 수 있음
    • 생산자 프로세스가 죽어있어도 소비자는 메시지를 수신할 수 있음
  • 메시지 브로커에 있는 메시지는 소비자(수신자)가 꺼낼 때까지 안전하게 보관됨: durability
    • (프로세스가 죽어 있어도) 메시지는 소비하기 전까지는 사라지지 않음
    • 따라서, 프로그램 간 통신은 시간과 독립적임
  • 통신이 이벤트에 의해 구동될 수 있음
    • 큐의 상태에 따라 프로그램을 제어할 수 있다. 예를 들어 메시지가 큐에 도착하는 즉시 프로그램이 시작되도록 설정할 수 있다. 또는 예를 들어 큐에서 특정 우선 순위 이상의 메시지가 10개가 되거나 임의의 우선 순위의 메시지가 10개가 될 때까지 프로그램이 시작되지 않도록 지정할 수 있다.
  • 확장에 용이함
    • 메시지 브로커는 여러 큐를 만들거나, 수평적으로 확장하여 메시지 부하 증가를 처리할 수 있다.

메시지 브로커의 단점

  • 프로세스 간의 직접 연결에 비해 아키텍처가 복잡합다.

메시지 브로커 선택의 기준

메시지 브로커의 종류로는 Apache Kafka, Amazon Kinesis, Amazon SQS 등이 있습니다. 어떤 메시지 브로커를 선택해야 할 경우, 다음 기준으로 선택하면 좋다.

  • 프로그래밍 언어 지원 여부
  • 메시징 표준 지원 여부
  • 메시지 순서 보장 여부
  • 전달 보장 여부: 어떤 종류의 전달을 보장하는가?
  • 영속성: 브로커가 고장나도 문제가 없도록 메시지가 - 디스크에 저장되는가?
  • 내구성: 소비자가 메시지 브로커에 다시 접속할 경우, - 접속이 중단된 시간에 전달된 메시지를 받을 수 있는가?
  • 확장성
  • 지연 시간

발표

메시지 서비스로는 대표적으로 Apache Kafka와 Amazon SQS, Amazon Kinesis가 있습니다. 각각은 어떤 차이가 있나요?

Kafka

  • Kafka는 상당히 확장 가능한 시스템이며 일괄 처리로 메시지를 보내려는 경우(대용량의 이벤트 스트림)높은 워크로드에 적합합니다.
  • Kafka 주제는 하나의 소비자 그룹에 있는 여러 소비자가 완전히 병렬로 읽을 수 있는 몇 개의 파티션으로 구성되어 있습니다.
  • 매우 좋은 성능을 제공합니다.
  • 예를 들어, 로드가 높은 스트리밍 시스템을 구축해야 하는 경우 Kafka가 정말 적합하다

SQS

  • 개별 메시지 별로 확인/실패가 필요한 경우
  • 소비자의 처리량을 동적으로 증가하고자 할 경우(Kinesis 는 샤드를 동적으로 추가하는것이 까다롭기 때문에 적합하지 않음)
  • SQS는 Amazon 관리형 서비스입니다(따라서 스스로 인프라를 지원할 필요가 없습니다.).
  • SQS는 일부 클라이언트의 메시지(이벤트)를 잡아야 할 때 이벤트에 더 적합하며 이 메시지는 대기열에서 자동으로 표시됩니다.
  • SQS는 Kafka만큼 빠르지 않으므로 작업 부하가 큰 워크로드에 적합하지 않습니다.
  • 초당 이벤트 수가 많지 않은 이벤트에 훨씬 더 적합합니다.
  • 예를 들어 일부 S3 파일 업로드에 반응하려는 경우 (이 파일의 일부 처리를 시작하기 위해) SQS는 매우 좋습니다.


Amazon Kinesis

  • 처리된 메시지를 다른 application이 다시 처리해야하는 경우 사용하기 좋다.
  • 대규모 데이터의 실시간 처리를 위한 스트리밍 데이터 수집 서비스이다.
  • 하나의 샤드가 마치 하나의 SQS와 같이 일을 한다.
  • Data Stream 내의 Shard를 늘려서 처리량 확장 가능하다.
  • 기본적으로 메시지는 24시간 보관하며, 최대 7일까지 보관 한다.
  • Stream당 Shard 수 제한은 없음, 하지만 리전당 Shard의 최대 개수제한이 있다.(AWS 요청시 확장 가능)
  • Shard당 1MiB/s 또는 1000개/s 쓰기 가능
  • Shard당 초당 5회의 GetRecords 호출 가능, 각각의 GetRecords는 10MiB/s 또는 10000개 읽기 가능, 최대 2MiB/s 읽기 가능하므로 GetRecords 10MiB 읽을 시 5초간 활성

표로 비교

웹 서비스에서 메시지 브로커를 이용해 비동기적인 방법이 활용되는 사례를 하나 이상 찾아보고, 어떻게 활용되는지 설명하세요.

예를 들어서, 페이스북 사진을 첨부해서 글을 올리면, 브라우저에서는 글이 등록되었다고는 하지만 바로 타임라인에 표시되지 않고 3 ~ 4초 후에 업데이트 되는 것을 확인할 수 있다.

이는 브라우저에서 글쓰기 요청을 받으면 먼저 클라이언트에게 완료되었다는 응답을 보낸 후 서버는 뒷단에서 그림의 크기와 형식을 조정하는 등의 추가적인 작업을 한후에 데이터베이스에 저장한 후 타임라인에 반영하기 때문이다. 이러한 처리가 대표적인 비동기 처리의 예이다.

참고) Amazon Web Services Korea Youtube , 한국 Elastic 사용자 그룹 Kafka

profile
내기 이해한 것을 보관하는 곳

0개의 댓글