마이크로서비스 아키텍처의 프로세스 간 통신

마이크로서비스 아키텍처로 애플리케이션을 구축하는 방법에 대한 시리즈의 세 번째 포스트입니다. 첫 번째 포스트에서는 마이크로서비스 아키텍처 패턴을 소개하고 모놀리식 아키텍처 패턴과 비교하며 마이크로서비스 사용의 장점과 단점에 대해 설명했습니다. 두 번째 포스트에서는 애플리케이션의 클라이언트가 API Gateway로 알려진 중개자를 통해 마이크로서비스와 통신하는 방법을 설명했습니다.

이 포스트에서는 시스템 내의 서비스(Inter-Process)가 서로 통신하는 방법을 살펴봅니다. 네 번째 포스트에서는 밀접하게 관련된 서비스 발견 문제를 탐구합니다.

목차

1. 소개
2. 상호 작용 스타일(Interaction Styles)
3. API 정의(Defining)
4. 진화하는(Evolving) API
5. 부분 실패 처리(Handling Partial Failure)
6. IPC 기술
6-1. 비동기식 메시지 기반 통신 (Asynchronous, Message‑Based Communication)
6-2. 동기식, 요청/응답 IPC (Synchronous, Request/Response IPC)
6-3. 메시지 형식 (Message Formats)
7. 요약

1. 소개

모놀리식 응용 프로그램에서 구성 요소는 언어 수준 ,Method 또는 함수 호출을 통해 서로를 호출합니다. 이와 대조적으로 마이크로서비스 기반 애플리케이션은 여러 시스템에서 실행되는 분산 시스템입니다. 각 서비스 인스턴스는 일반적으로 프로세스입니다. 따라서 다음 다이어그램에서 볼 수 있듯이 서비스는 IPC(프로세스 간 통신) 메커니즘을 사용하여 상호 작용해야 합니다.

microservices-part3_monolith vs microservices

나중에 특정 IPC 기술을 살펴보겠지만 먼저 다양한 설계 문제를 살펴보겠습니다.

2. 상호 작용 스타일(Interaction Styles)

서비스에 대한 IPC 메커니즘을 선택할 때 서비스가 상호 작용하는 방식에 대해 먼저 생각하는 것이 유용합니다. 다양한 클라이언트⇔서비스 상호작용 스타일이 있습니다. 두 가지 차원으로 분류할 수 있습니다. 첫 번째 차원은 상호 작용이 일대일(one-to-one)인지 일대다(one-to-many)인지입니다.

  • 일대일(one-to-one) – 각 클라이언트 요청은 정확히 하나의 서비스 인스턴스에 의해 처리됩니다.
  • 일대다(one-to-many) – 각 요청은 여러 서비스 인스턴스에서 처리됩니다.

두 번째 차원은 상호 작용이 동기식(Synchronous)인지 비동기식(Asynchronous)인지 여부입니다.

  • 동기(Synchronous) – 클라이언트는 서비스로부터 시기적절한 응답을 기대하며 기다리는 동안 차단할 수도 있습니다.
  • 비동기식(Asynchronous) – 클라이언트는 응답을 기다리는 동안 차단하지 않으며 응답이 있는 경우 응답이 반드시 즉시 전송되지는 않습니다.

다음 표는 다양한 상호 작용 스타일을 보여줍니다.

One-to-OneOne-to-Many
SynchronousRequest/response
AsynchronousNotificationPublish/subscribe
AsynchronousRequest/async responsePublish/async responses

일대일(one-to-one) 상호 작용에는 다음과 같은 종류가 있습니다.

  • 요청/응답(Request/response) – 클라이언트가 서비스에 요청하고 응답을 기다립니다. 클라이언트는 응답이 적시에 도착하기를 기대합니다. Thread 기반 응용 프로그램에서 요청을 만드는 Thread는 기다리는 동안 차단될 수도 있습니다.
  • 알림(Notification) (one-way request) – 클라이언트가 서비스에 요청을 보내지만 응답이 예상되거나 전송되지 않습니다.
  • 요청/비동기 응답(Request/async response) – 클라이언트는 비동기적으로 응답하는 서비스에 요청을 보냅니다. 클라이언트는 기다리는 동안 차단되지 않으며 응답이 잠시 동안 도착하지 않을 수 있다는 가정 하에 설계되었습니다.

일대다 상호 작용에는 다음과 같은 종류가 있습니다.

  • 게시/구독(Publish/subscribe) – 클라이언트가 알림 메시지를 게시하며, 이는 0개 이상의 관심 서비스에서 사용됩니다.
  • 게시/비동기 응답(Publish/async responses) – 클라이언트는 요청 메시지를 게시한 다음 관심 있는 서비스의 응답을 일정 시간 동안 기다립니다.

각 서비스는 일반적으로 이러한 상호 작용 스타일의 조합을 사용합니다. 일부 서비스의 경우 단일 IPC 메커니즘으로 충분합니다. 다른 서비스는 IPC 메커니즘의 조합을 사용해야 할 수도 있습니다. 다음 다이어그램은 사용자가 여행을 요청할 때 택시 호출 애플리케이션의 서비스가 상호 작용하는 방식을 보여줍니다.

microservices taxi service

서비스는 알림, 요청/응답 및 Publish/subscribe의 조합을 사용합니다. 예를 들어 승객의 스마트폰은 픽업을 요청하기 위해 여행 관리 서비스에 알림을 보냅니다. 여행 관리 서비스는 요청/응답을 사용하여 승객 서비스를 호출하여 승객의 계정이 활성 상태인지 확인합니다. 그런 다음 여행 관리 서비스는 여행을 만들고 Publish/subscribe을 사용하여 사용 가능한 드라이버를 찾는 디스패처(Dispatche)를 비롯한 다른 서비스에 알립니다.

이제 상호작용 스타일을 살펴보았으므로 API를 정의하는 방법을 살펴보겠습니다.

3. API 정의(Defining)

서비스의 API는 서비스와 클라이언트 간의 계약입니다. IPC 메커니즘의 선택에 관계없이 일종의 IDL(인터페이스 정의 언어)을 사용하여 서비스의 API를 정확하게 정의하는 것이 중요합니다. 서비스 정의에 API 우선 접근 방식을 사용하는 것에 대한 좋은 주장도 있습니다. 인터페이스 정의를 작성하고 클라이언트 개발자와 검토하여 서비스 개발을 시작합니다. 서비스를 구현하는 것은 API 정의를 반복한 후에만 가능합니다. 이 디자인을 미리 수행하면 클라이언트의 요구 사항을 충족하는 서비스를 구축할 가능성이 높아집니다.

이 포스트의 뒷부분에서 볼 수 있듯이 API 정의의 특성은 사용 중인 IPC 메커니즘에 따라 다릅니다. 메시징을 사용하는 경우 API는 메시지 채널과 메시지 유형으로 구성됩니다. HTTP를 사용하는 경우 API는 URL과 요청 및 응답 형식으로 구성됩니다. 나중에 우리는 일부 IDL에 대해 더 자세히 설명할 것입니다.

4. 진화하는(Evolving) API

서비스의 API는 시간이 지남에 따라 변함없이 변경됩니다. 모놀리식 애플리케이션에서는 일반적으로 API를 변경하고 모든 호출자를 업데이트하는 것이 간단합니다. 마이크로서비스 기반 애플리케이션에서는 API의 모든 소비자가 동일한 애플리케이션의 다른 서비스인 경우에도 훨씬 더 어렵습니다. 일반적으로 모든 클라이언트가 서비스와 함께 업그레이드하도록 강제할 수는 없습니다. 또한 서비스의 이전 버전과 새 버전이 동시에 실행되도록 새 버전의 서비스를 점진적으로 배포할 것입니다. 이러한 문제를 처리하기 위한 전략을 갖는 것이 중요합니다.

API 변경을 처리하는 방법은 변경 크기에 따라 다릅니다. 일부 변경 사항은 사소한 것이며 이전 버전과 역호환됩니다. 예를 들어 요청이나 응답에 속성을 추가할 수 있습니다. 견고성 원칙을 준수하도록 클라이언트와 서비스를 설계하는 것이 합리적입니다. 이전 API를 사용하는 클라이언트는 계속해서 새 버전의 서비스를 사용해야 합니다. 서비스는 누락된 요청 속성에 대한 기본값을 제공하고 클라이언트는 추가 응답 속성을 무시합니다. API를 쉽게 발전시킬 수 있는 IPC 메커니즘과 메시징 형식을 사용하는 것이 중요합니다.

그러나 때로는 API에 대해 호환되지 않는 주요 변경을 수행해야 합니다. 클라이언트에게 즉시 업그레이드를 강제할 수 없기 때문에 서비스는 일정 기간 동안 이전 버전의 API를 지원해야 합니다. REST와 같은 HTTP 기반 메커니즘을 사용하는 경우 한 가지 방법은 URL에 버전 번호를 포함하는 것입니다. 각 서비스 인스턴스는 여러 버전을 동시에 처리할 수 있습니다. 또는 각각 특정 버전을 처리하는 서로 다른 인스턴스를 배포할 수 있습니다.

5. 부분 실패 처리(Handling Partial Failure)

API Gateway에 대한 이전 포스트에서 언급했듯이 분산 시스템에는 부분적 실패의 위험이 항상 존재합니다. 클라이언트와 서비스는 별개의 프로세스이기 때문에 서비스는 클라이언트의 요청에 적시에 응답하지 못할 수 있습니다. 장애 또는 유지 보수로 인해 서비스가 중단될 수 있습니다. 또는 서비스가 과부하되어 요청에 매우 느리게 응답할 수 있습니다.

예를 들어 해당 포스트의 제품 세부 정보 시나리오를 고려하십시오. 추천 서비스가 응답하지 않는다고 가정해 보겠습니다. 클라이언트의 순진한 구현은 응답을 무한정 기다리는 것을 차단할 수 있습니다. 그러면 사용자 경험이 좋지 않을 뿐만 아니라 많은 응용 프로그램에서 Thread와 같은 귀중한 리소스를 소모하게 됩니다. 결국 런타임에 Thread가 부족해지고 다음 그림과 같이 응답하지 않게 됩니다.

microservices threadsblocked

이 문제를 방지하려면 부분 오류를 처리하도록 서비스를 설계하는 것이 중요합니다.

따라야 할 좋은 접근 방식은 Netflix에서 설명한 것입니다. 부분 실패를 처리하기 위한 전략은 다음과 같습니다.

  • 네트워크 시간 초과(Network timeouts) – 무기한 차단하지 않고 응답을 기다릴 때 항상 시간 초과를 사용합니다. 시간 초과를 사용하면 리소스가 무기한으로 묶이지 않습니다.
  • 미해결 요청 수 제한(Limiting the number of outstanding requests) – 클라이언트가 특정 서비스에 대해 가질 수 있는 미해결 요청 수에 상한선을 부과합니다. 제한에 도달한 경우 추가 요청을 하는 것은 무의미할 수 있으며 이러한 시도는 즉시 실패해야 합니다.
  • 회로 차단기 패턴(Circuit breaker pattern) – 성공 및 실패한 요청 수를 추적합니다. 오류율이 구성된 임계값을 초과하면 추가 시도가 즉시 실패하도록 회로 차단기를 트립합니다. 많은 수의 요청이 실패하면 서비스를 사용할 수 없고 요청을 보내는 것이 무의미하다는 의미입니다. 시간 초과 기간 후에 클라이언트는 다시 시도하고 성공하면 회로 차단기를 닫아야 합니다.
  • 대체 제공(Provide fallbacks) – 요청이 실패할 때 대체 논리를 수행합니다. 예를 들어 캐시된 데이터 또는 빈 권장 사항 세트와 같은 기본값을 반환합니다.

Netflix Hystrix는 이러한 패턴과 기타 패턴을 구현하는 오픈소스 라이브러리입니다. JVM을 사용하는 경우 Hystrix 사용을 반드시 고려해야 합니다. 또한 JVM이 아닌 환경에서 실행 중인 경우 동등한 라이브러리를 사용해야 합니다.

6. IPC 기술

선택할 수 있는 다양한 IPC 기술이 있습니다. 서비스는 HTTP 기반 REST 또는 Thrift와 같은 동기 요청/응답 기반 통신 메커니즘을 사용할 수 있습니다. 또는 AMQP 또는 STOMP와 같은 비동기 메시지 기반 통신 메커니즘을 사용할 수 있습니다. 또한 다양한 메시지 형식이 있습니다. 서비스는 JSON 또는 XML과 같은 사람이 읽을 수 있는 텍스트 기반 형식을 사용할 수 있습니다. 또는 Avro 또는 프로토콜 버퍼와 같은 이진 형식(더 효율적인)을 사용할 수 있습니다. 나중에 동기 IPC 메커니즘을 살펴보겠지만 먼저 비동기 IPC 메커니즘에 대해 논의하겠습니다.

6-1. 비동기식 메시지 기반 통신 (Asynchronous, Message‑Based Communication)

메시징을 사용할 때 프로세스는 비동기적으로 메시지를 교환하여 통신합니다. 클라이언트는 메시지를 보내 서비스에 요청합니다. 서비스가 응답할 것으로 예상되면 별도의 메시지를 클라이언트에 다시 전송하여 응답합니다. 통신은 비동기식이므로 클라이언트는 응답 대기를 차단하지 않습니다. 대신 클라이언트는 응답이 즉시 수신되지 않는다는 가정하에 작성됩니다.

메시지는 헤더(발신자와 같은 메타데이터)와 메시지 본문으로 구성됩니다. 메시지는 채널을 통해 교환됩니다. 원하는 수의 생산자가 채널에 메시지를 보낼 수 있습니다. 마찬가지로, 원하는 수의 소비자가 채널에서 메시지를 받을 수 있습니다. 채널에는 지점 간 및 Publish-subscribe의 두 가지 종류가 있습니다. 지점 간 채널은 채널에서 읽고 있는 소비자 중 정확히 한 명에게 메시지를 전달합니다. 서비스는 앞에서 설명한 일대일 상호 작용 스타일에 대해 지점 간 채널을 사용합니다. Publish-subscribe 채널은 연결된 모든 소비자에게 각 메시지를 전달합니다. 서비스는 위에서 설명한 일대다 상호 작용 스타일에 대해 Publish-subscribe 채널을 사용합니다.

다음 다이어그램은 택시 호출 애플리케이션이 Publish-subscribe 채널을 사용하는 방법을 보여줍니다.

microservices pub sub channels

여행 관리 서비스는 Publish-subscribe 채널에 여행 생성 메시지를 작성하여 새로운 여행에 대해 Dispatcher와 같은 관심 있는 서비스에 알립니다. Dispatcher는 사용 가능한 드라이버를 찾고 게시-구독 채널에 드라이버 제안 메시지를 작성하여 다른 서비스에 알립니다.

선택할 수 있는 메시징 시스템이 많이 있습니다. 다양한 프로그래밍 언어를 지원하는 언어를 선택해야 합니다. 일부 메시징 시스템은 AMQP 및 STOMP와 같은 표준 프로토콜을 지원합니다. 다른 메시징 시스템에는 독점적이지만 문서화된 프로토콜이 있습니다. RabbitMQ, Apache Kafka, Apache ActiveMQ 및 NSQ를 포함하여 선택할 수 있는 수많은 오픈 소스 메시징 시스템이 있습니다. 높은 수준에서 그들은 모두 어떤 형태의 메시지와 채널을 지원합니다. 그들은 모두 안정적이고 고성능이며 확장 가능하기 위해 노력합니다. 그러나 각 브로커의 메시징 모델의 세부 사항에는 상당한 차이가 있습니다.

메시징을 사용하면 다음과 같은 많은 이점이 있습니다.

  • 서비스에서 클라이언트 분리 – 클라이언트는 단순히 메시지를 적절한 채널로 전송하여 요청합니다. 클라이언트는 서비스 인스턴스를 완전히 인식하지 못합니다. 서비스 인스턴스의 위치를 결정하기 위해 검색 메커니즘을 사용할 필요가 없습니다.
  • 메시지 버퍼링 – HTTP와 같은 동기식 요청/응답 프로토콜을 사용하면 교환 기간 동안 클라이언트와 서비스를 모두 사용할 수 있어야 합니다. 대조적으로 메시지 브로커는 소비자가 처리할 수 있을 때까지 채널에 작성된 메시지를 대기열에 넣습니다. 이는 예를 들어 주문 처리 시스템이 느리거나 사용할 수 없는 경우에도 온라인 상점이 고객의 주문을 수락할 수 있음을 의미합니다. 주문 메시지는 단순히 대기열에 있습니다.
  • 유연한 클라이언트-서비스 상호 작용 – 메시징은 앞에서 설명한 모든 상호 작용 스타일을 지원합니다.
  • 명시적 프로세스 간 통신 – RPC 기반 메커니즘은 원격 서비스 호출이 로컬 서비스 호출과 동일하게 보이도록 시도합니다. 그러나 물리 법칙과 부분적 실패 가능성 때문에 실제로는 상당히 다릅니다. 메시징은 이러한 차이를 매우 명확하게 하여 개발자가 잘못된 보안 감각에 속아 넘어가지 않도록 합니다.

그러나 메시징 사용에는 몇 가지 단점이 있습니다.

  • 추가적인 운영 복잡성 – 메시징 시스템은 설치, 구성 및 운영되어야 하는 또 다른 시스템 구성 요소입니다. 메시지 브로커는 고가용성이어야 하며 그렇지 않으면 시스템 안정성이 영향을 받습니다.
  • 요청/응답 기반 상호 작용 구현의 복잡성 – 요청/응답 스타일 상호 작용을 구현하려면 약간의 작업이 필요합니다. 각 요청 메시지에는 응답 채널 식별자와 상관 식별자가 포함되어야 합니다. 서비스는 상관 ID를 포함하는 응답 메시지를 응답 채널에 씁니다. 클라이언트는 상관 ID를 사용하여 응답을 요청과 일치시킵니다. 요청/응답을 직접 지원하는 IPC 메커니즘을 사용하는 것이 더 쉬운 경우가 많습니다.

메시징 기반 IPC 사용에 대해 살펴보았으므로 이제 요청/응답 기반 IPC를 살펴보겠습니다.

6-2. 동기식, 요청/응답 IPC (Synchronous, Request/Response IPC)

동기식 요청/응답 기반 IPC 메커니즘을 사용할 때 클라이언트는 서비스에 요청을 보냅니다. 서비스는 요청을 처리하고 응답을 다시 보냅니다. 많은 클라이언트에서 요청을 만드는 스레드는 응답을 기다리는 동안 차단됩니다. 다른 클라이언트는 Futures 또는 Rx Observables에 의해 캡슐화될 수 있는 비동기 이벤트 구동 클라이언트 코드를 사용할 수 있습니다. 그러나 메시징을 사용할 때와 달리 클라이언트는 응답이 적시에 도착한다고 가정합니다. 선택할 수 있는 프로토콜이 많이 있습니다. 널리 사용되는 두 가지 프로토콜은 REST와 Thrift입니다. 먼저 REST를 살펴보자.

REST

오늘날 RESTful 스타일로 API를 개발하는 것이 유행입니다. REST는 (거의 항상) HTTP를 사용하는 IPC 메커니즘입니다. REST의 핵심 개념은 일반적으로 고객 또는 제품과 같은 비즈니스 개체 또는 비즈니스 개체 컬렉션을 나타내는 리소스입니다. REST는 URL을 사용하여 참조되는 리소스를 조작하기 위해 HTTP 동사를 사용합니다. 예를 들어, GET 요청은 XML 문서 또는 JSON 객체의 형태일 수 있는 리소스의 표현을 반환합니다. POST 요청은 새 리소스를 생성하고 PUT 요청은 리소스를 업데이트합니다. REST의 창시자인 Roy Fielding의 말을 인용하자면:

“REST는 전체적으로 적용될 때 구성 요소 상호 작용의 확장성, 인터페이스의 일반성, 구성 요소의 독립적 배포 및 중간 구성 요소를 강조하여 상호 작용 대기 시간을 줄이고 보안을 강화하며 레거시 시스템을 캡슐화하는 일련의 아키텍처 제약 조건을 제공합니다.”

다음 다이어그램은 택시 호출 애플리케이션이 REST를 사용할 수 있는 방법 중 하나를 보여줍니다.

microservices rest

승객의 스마트폰은 여행 관리 서비스의 /trips 리소스에 POST 요청을 하여 여행을 요청합니다. 이 서비스는 승객에 대한 정보에 대한 GET 요청을 승객 관리 서비스로 전송하여 요청을 처리합니다. 승객이 여행을 만들 수 있는 권한이 있는지 확인한 후 여행 관리 서비스는 여행을 만들고 스마트폰에 201 응답을 반환합니다.

많은 개발자가 HTTP 기반 API가 RESTful이라고 주장합니다. 그러나 Fielding이 이 블로그 게시물에서 설명하는 것처럼 모두가 실제로 그런 것은 아닙니다. Leonard Richardson(관계 없음)은 다음 수준으로 구성된 REST에 대한 매우 유용한 성숙도 모델을 정의합니다.

  • Level 0 – 수준 0 API의 클라이언트는 유일한 URL 끝점에 HTTP POST 요청을 만들어 서비스를 호출합니다. 각 요청은 수행할 작업, 작업 대상(예: 비즈니스 개체) 및 매개변수를 지정합니다.
  • Level 1 – 레벨 1 API는 리소스 아이디어를 지원합니다. 리소스에 대한 작업을 수행하기 위해 클라이언트는 수행할 작업과 매개변수를 지정하는 POST 요청을 수행합니다.
  • Level 2 – 레벨 2 API는 HTTP 동사를 사용하여 GET 검색, POST 생성, PUT 업데이트 등의 작업을 수행합니다. 요청 쿼리 매개변수와 본문(있는 경우)은 작업의 매개변수를 지정합니다. 이를 통해 서비스는 GET 요청에 대한 캐싱과 같은 웹 인프라를 활용할 수 있습니다.
  • Level 3 – 레벨 3 API의 디자인은 HATEOAS(Hypertext As Engine Of Application State) 원칙에 기반을 두고 있습니다. 기본 아이디어는 GET 요청에 의해 반환된 리소스 표현에 해당 리소스에 대해 허용되는 작업을 수행하기 위한 링크가 포함된다는 것입니다. 예를 들어, 클라이언트는 주문을 검색하기 위해 보낸 GET 요청에 대한 응답으로 반환된 주문 표현의 링크를 사용하여 주문을 취소할 수 있습니다. HATEOAS의 이점은 더 이상 URL을 클라이언트 코드에 고정할 필요가 없다는 것입니다. 또 다른 이점은 리소스 표현에 허용 가능한 작업에 대한 링크가 포함되어 있기 때문에 클라이언트가 현재 상태

HTTP 기반 프로토콜을 사용하면 다음과 같은 많은 이점이 있습니다.

  • HTTP는 간단하고 친숙합니다.
  • Postman과 같은 확장을 사용하여 브라우저 내에서 또는 curl을 사용하여 명령줄에서 HTTP API를 테스트할 수 있습니다(JSON 또는 기타 텍스트 형식이 사용된다고 가정).
  • 요청/응답 방식의 통신을 직접 지원합니다.
  • 물론 HTTP는 방화벽 친화적입니다.
  • 시스템 아키텍처를 단순화하는 중간 브로커가 필요하지 않습니다.

HTTP 사용에는 몇 가지 단점이 있습니다.

  • 요청/응답 방식의 상호 작용만 직접 지원합니다. 알림에 HTTP를 사용할 수 있지만 서버는 항상 HTTP 응답을 보내야 합니다.
  • 클라이언트와 서비스는 메시지를 버퍼링하는 중개자 없이 직접 통신하기 때문에 교환 기간 동안 둘 다 실행되어야 합니다.
  • 클라이언트는 각 서비스 인스턴스의 위치(즉, URL)를 알아야 합니다. API 게이트웨이에 대한 이전 기사에서 설명한 것처럼 이것은 최신 애플리케이션에서 사소한 문제입니다. 클라이언트는 서비스 검색 메커니즘을 사용하여 서비스 인스턴스를 찾아야 합니다.

개발자 커뮤니티는 최근 RESTful API에 대한 인터페이스 정의 언어의 가치를 재발견했습니다. RAML 및 Swagger를 포함한 몇 가지 옵션이 있습니다. Swagger와 같은 일부 IDL을 사용하면 요청 및 응답 메시지 형식을 정의할 수 있습니다. RAML과 같은 다른 것들은 JSON Schema와 같은 별도의 사양을 사용해야 합니다. API를 설명할 뿐만 아니라 IDL에는 일반적으로 인터페이스 정의에서 클라이언트 스텁과 서버 스켈레톤을 생성하는 도구가 있습니다.

Thrift

Apache Thrift는 REST에 대한 흥미로운 대안입니다. 언어 간 RPC 클라이언트 및 서버를 작성하기 위한 프레임워크입니다. Thrift는 API 정의를 위한 C 스타일 IDL을 제공합니다. Thrift 컴파일러를 사용하여 클라이언트 측 스텁과 서버 측 스켈레톤을 생성합니다. 컴파일러는 C++, Java, Python, PHP, Ruby, Erlang 및 Node.js를 비롯한 다양한 언어에 대한 코드를 생성합니다.

Thrift 인터페이스는 하나 이상의 서비스로 구성됩니다. 서비스 정의는 Java 인터페이스와 유사합니다. 강력한 형식의 메서드 모음입니다. Thrift 메서드는 (무효일 수 있음) 값을 반환하거나 단방향으로 정의할 수 있습니다. 값을 반환하는 메서드는 상호 작용의 요청/응답 스타일을 구현합니다. 클라이언트는 응답을 기다리며 예외를 throw할 수 있습니다. 단방향 방법은 알림 스타일의 상호 작용에 해당합니다. 서버는 응답을 보내지 않습니다.

Thrift는 JSON, 바이너리 및 컴팩트 바이너리와 같은 다양한 메시지 형식을 지원합니다. 바이너리는 디코딩이 더 빠르기 때문에 JSON보다 더 효율적입니다. 그리고 이름에서 알 수 있듯이 컴팩트 바이너리는 공간 효율적인 형식입니다. 물론 JSON은 사람과 브라우저 친화적입니다. Thrift는 또한 원시 TCP 및 HTTP를 포함한 전송 프로토콜을 선택할 수 있도록 합니다. 원시 TCP는 HTTP보다 더 효율적일 수 있습니다. 그러나 HTTP는 방화벽, 브라우저 및 인간 친화적입니다.

6-3. 메시지 형식 (Message Formats)

이제 HTTP와 Thrift를 살펴보았으므로 메시지 형식의 문제를 살펴보겠습니다. 메시징 시스템 또는 REST를 사용하는 경우 메시지 형식을 선택하게 됩니다. Thrift와 같은 다른 IPC 메커니즘은 적은 수의 메시지 형식만 지원할 수 있습니다. 두 경우 모두 언어 간 메시지 형식을 사용하는 것이 중요합니다. 현재 단일 언어로 마이크로서비스를 작성하고 있더라도 미래에는 다른 언어를 사용할 가능성이 높습니다.

메시지 형식에는 텍스트와 바이너리의 두 가지 주요 유형이 있습니다. 텍스트 기반 형식의 예로는 JSON 및 XML이 있습니다. 이러한 형식의 장점은 사람이 읽을 수 있을 뿐만 아니라 자체적으로 설명할 수 있다는 것입니다. JSON에서 객체의 속성은 이름-값 쌍의 컬렉션으로 표현됩니다. 마찬가지로 XML에서 속성은 명명된 요소와 값으로 표현됩니다. 이를 통해 메시지 소비자는 관심 있는 값을 선택하고 나머지는 무시할 수 있습니다. 결과적으로 메시지 형식에 대한 사소한 변경은 이전 버전과 쉽게 호환될 수 있습니다.

XML 문서의 구조는 XML 스키마에 의해 지정됩니다. 시간이 지남에 따라 개발자 커뮤니티는 JSON에도 유사한 메커니즘이 필요하다는 것을 깨닫게 되었습니다. 한 가지 옵션은 독립 실행형 또는 Swagger와 같은 IDL의 일부로 JSON 스키마를 사용하는 것입니다.

텍스트 기반 메시지 형식 사용의 단점은 메시지, 특히 XML이 장황한 경향이 있다는 것입니다. 메시지가 자체 설명적이기 때문에 모든 메시지에는 속성 값과 함께 속성 이름이 포함됩니다. 또 다른 단점은 텍스트 구문 분석의 오버헤드입니다. 따라서 이진 형식 사용을 고려할 수 있습니다.

선택할 수 있는 여러 바이너리 형식이 있습니다. Thrift RPC를 사용하는 경우 바이너리 Thrift를 사용할 수 있습니다. 메시지 형식을 선택하는 경우 많이 사용되는 옵션에는 프로토콜 버퍼 및 Apache Avro가 있습니다. 이 두 형식 모두 메시지 구조를 정의하기 위한 형식화된 IDL을 제공합니다. 그러나 한 가지 차이점은 프로토콜 버퍼는 태그가 지정된 필드를 사용하는 반면 Avro 소비자는 메시지를 해석하기 위해 스키마를 알아야 한다는 것입니다. 결과적으로 API 진화는 Avro보다 프로토콜 버퍼를 사용하는 것이 더 쉽습니다.

7. 요약

마이크로서비스는 프로세스 간 통신 메커니즘을 사용하여 통신해야 합니다. 서비스가 통신하는 방식을 설계할 때 서비스가 상호 작용하는 방식, 각 서비스에 대한 API를 지정하는 방법, API를 발전시키는 방법, 부분적 오류를 처리하는 방법 등 다양한 문제를 고려해야 합니다. 마이크로 서비스가 사용할 수 있는 IPC 메커니즘에는 비동기 메시징과 동기 요청/응답의 두 가지 종류가 있습니다. 시리즈의 다음 포스트에서는 마이크로서비스 아키텍처에서 서비스 검색 문제를 살펴보겠습니다.

NGINX STORE 뉴스레터 및 최신 소식 구독하기

* indicates required