블로그 | NGINX

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

NGINX-F5-수평-검정-유형-RGB의 일부
크리스 리차드슨 썸네일
크리스 리차드슨
2015년 7월 24일 게시

편집자 – 이 7부작 시리즈 기사는 이제 완료되었습니다.

  1. 마이크로서비스 소개
  2. 마이크로서비스 구축: API 게이트웨이 사용
  3. 마이크로서비스 구축: 마이크로서비스 아키텍처에서의 프로세스 간 통신(본 문서)
  4. 마이크로서비스 아키텍처에서의 서비스 발견
  5. 마이크로서비스를 위한 이벤트 기반 데이터 관리
  6. 마이크로서비스 배포 전략 선택
  7. 모놀리스를 마이크로서비스로 리팩토링

NGINX Plus를 사용하여 마이크로서비스를 구현하는 것에 대한 정보와 더불어 전체 문서 세트를 전자책으로 다운로드할 수도 있습니다. – 마이크로서비스: 설계부터 배포까지 . 또한 새로운 마이크로서비스 솔루션 페이지 를 확인해 보세요.

이 글은 마이크로서비스 아키텍처를 사용하여 애플리케이션을 구축하는 방법에 대한 시리즈의 세 번째 기사입니다. 첫 번째 기사 에서는 마이크로서비스 아키텍처 패턴을 소개하고, 이를 모놀리식 아키텍처 패턴 과 비교하며, 마이크로서비스를 사용하는 것의 이점과 단점에 대해 설명합니다. 두 번째 기사에서는 애플리케이션의 클라이언트가 API 게이트웨이 라는 중개자를 통해 마이크로서비스와 통신하는 방법을 설명합니다. 이 글에서는 시스템 내의 서비스들이 서로 통신하는 방식을 살펴보겠습니다. 네 번째 기사에서는 서비스 발견과 밀접하게 관련된 문제를 살펴봅니다.

소개

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


마이크로서비스 애플리케이션에서 서비스는 IPC(프로세스 간 통신) 메커니즘이 필요합니다(모놀리스의 모듈은 루틴을 호출할 수 있음)

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

상호작용 스타일

서비스에 대한 IPC 메커니즘을 선택할 때, 먼저 서비스가 어떻게 상호 작용하는지 생각해 보는 것이 좋습니다. 클라이언트⇔서비스 상호작용 스타일에는 다양한 것이 있습니다. 이는 두 가지 차원에 따라 분류될 수 있다. 첫 번째 차원은 상호작용이 일대일인지 일대다인지입니다.

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

두 번째 차원은 상호작용이 동기식인지 비동기식인지입니다.

  • 동기식 – 클라이언트는 서비스로부터 적시에 응답을 기대하며 기다리는 동안 차단될 수도 있습니다.
  • 비동기 - 클라이언트는 응답을 기다리는 동안 차단되지 않으며, 응답이 있더라도 즉시 전송되지 않을 수 있습니다.

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

  일대일 일대다
동기식 요청/응답  — 
비동기 공고 게시/구독
요청/비동기 응답 게시/비동기 응답

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

  • 요청/응답 – 클라이언트가 서비스에 요청을 하고 응답을 기다립니다. 고객은 적절한 시기에 답변이 도착하기를 기대합니다. 스레드 기반 애플리케이션에서 요청을 하는 스레드가 대기하는 동안 차단될 수도 있습니다.
  • 알림(일방향 요청이라고도 함) - 클라이언트가 서비스에 요청을 보내지만 답변을 기대하거나 전송하지 않습니다.
  • 요청/비동기 응답 – 클라이언트가 서비스에 요청을 보내고 서비스는 비동기적으로 응답합니다. 클라이언트는 대기하는 동안 차단되지 않으며, 응답이 잠시 도착하지 않을 수도 있다는 가정 하에 설계되었습니다.

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

  • 게시/구독 – 클라이언트가 알림 메시지를 게시하고, 이 메시지는 0개 이상의 관심 있는 서비스에서 사용됩니다.
  • 게시/비동기 응답 – 클라이언트는 요청 메시지를 게시한 다음 관심 있는 서비스로부터 응답을 받기 위해 일정 시간 동안 기다립니다.

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


이 서비스는 알림, 요청/응답, 게시/구독을 조합하여 사용합니다. 예를 들어, 승객의 스마트폰은 여행 관리 서비스에 픽업을 요청하는 알림을 보냅니다. 여행 관리 서비스는 요청/응답을 사용하여 승객 서비스를 호출하여 승객 계정이 활성화되어 있는지 확인합니다. 그런 다음 여행 관리 서비스에서 여행을 생성하고 게시/구독을 사용하여 디스패처를 포함한 다른 서비스에 알립니다. 디스패처는 이용 가능한 운전자를 찾습니다.

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

API 정의

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

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

진화하는 API

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

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

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

부분적 실패 처리

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

예를 들어, 해당 기사의 제품 세부 정보 시나리오를 생각해 보겠습니다. 추천 서비스가 응답하지 않는다고 가정해 보겠습니다. 클라이언트를 순진하게 구현하면 응답을 기다리며 무기한 차단될 수 있습니다. 그렇게 되면 사용자 경험이 나빠질 뿐만 아니라, 많은 애플리케이션에서는 스레드와 같은 귀중한 리소스를 소모하게 됩니다. 결국 런타임은 스레드가 고갈되어 다음 그림과 같이 응답하지 않게 됩니다.


마이크로서비스 앱은 부분적 실패를 처리하도록 설계되어야 하며 그렇지 않으면 클라이언트가 응답하지 않는 서비스를 기다리며 차단될 때 런타임에 스레드가 부족해질 수 있습니다.

이러한 문제를 방지하려면 부분적인 실패를 처리할 수 있도록 서비스를 설계하는 것이 필수적입니다.

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

  • 네트워크 시간 초과 - 절대로 무기한 차단하지 말고 응답을 기다릴 때는 항상 시간 초과를 사용하세요. 타임아웃을 사용하면 리소스가 무기한으로 묶여 있는 일이 발생하지 않습니다.
  • 미처리 요청 수 제한 – 클라이언트가 특정 서비스에 대해 가질 수 있는 미처리 요청 수에 상한을 설정합니다. 한도에 도달한 경우 추가 요청을 하는 것은 무의미하며, 그러한 시도는 즉시 실패해야 합니다.
  • 회로 차단기 패턴 – 성공한 요청과 실패한 요청의 수를 추적합니다. 오류율이 구성된 임계값을 초과하면 회로 차단기를 작동시켜 추가 시도가 즉시 실패하도록 합니다. 많은 수의 요청이 실패하는 경우 서비스를 사용할 수 없으며, 요청을 보내는 것이 무의미하다는 것을 의미합니다. 시간 초과가 지난 후 클라이언트는 다시 시도해야 하며, 성공하면 회로 차단기를 닫아야 합니다.
  • 폴백 제공 – 요청이 실패하면 폴백 논리를 수행합니다. 예를 들어, 캐시된 데이터나 빈 추천 세트와 같은 기본값을 반환합니다.

Netflix Hystrix 는 이러한 패턴과 기타 패턴을 구현하는 오픈 소스 라이브러리입니다. JVM을 사용하고 있다면 Hystrix를 꼭 고려해 보세요. JVM이 아닌 환경에서 실행하는 경우 동등한 라이브러리를 사용해야 합니다.

IPC 기술

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

비동기, 메시지 기반 통신

메시징을 사용할 때 프로세스는 비동기적으로 메시지를 교환하여 통신합니다. 클라이언트는 메시지를 보내 서비스에 요청을 합니다. 서비스에서 답변이 필요한 경우, 클라이언트에게 별도의 메시지를 보내 답변합니다. 통신이 비동기적으로 진행되므로 클라이언트는 응답을 기다리느라 멈추지 않습니다. 대신, 의뢰인은 답변을 즉시 받지 못할 것이라는 가정 하에 글을 씁니다.

메시지는 헤더(발신자 등의 메타데이터)와 메시지 본문으로 구성됩니다. 메시지는 채널을 통해 교환됩니다. 여러 생산자가 채널에 메시지를 보낼 수 있습니다. 마찬가지로, 아무리 많은 소비자라도 채널로부터 메시지를 받을 수 있습니다. 채널에는 지점 간 채널게시-구독 채널 두 가지가 있습니다. 지점 간 채널은 채널을 통해 메시지를 읽는 소비자 중 단 한 명에게만 메시지를 전달합니다. 서비스는 앞서 설명한 일대일 상호작용 스타일을 위해 지점 간 채널을 사용합니다. 게시-구독 채널은 각 메시지를 연결된 모든 소비자에게 전달합니다. 서비스는 위에 설명된 일대다 상호작용 스타일을 위해 게시-구독 채널을 사용합니다.

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


택시 호출 애플리케이션의 마이크로서비스는 디스패처와 다른 서비스 간의 통신을 위해 게시-구독 채널을 사용합니다.

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

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

메시징을 사용하는 데는 여러 가지 장점이 있습니다.

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

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

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

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

동기, 요청/응답 IPC

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

나머지

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

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

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


마이크로서비스 기반 택시 호출 앱에서 승객 스마트폰은 POST 요청을 보내고 여행 관리 마이크로서비스는 이를 승객 확인 마이크로서비스에 대한 GET 요청으로 변환합니다.

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

많은 개발자들은 자신의 HTTP 기반 API가 RESTful하다고 주장합니다. 하지만 필딩이 이 블로그 게시물 에서 설명했듯이, 실제로는 그런 경우가 전부는 아닙니다. Leonard Richardson(관계 없음)은 다음 수준으로 구성된 REST에 대한 매우 유용한 성숙도 모델을 정의했습니다.

  • 레벨 0 – 레벨 0 API의 클라이언트는 유일한 URL 엔드포인트에 HTTP POST 요청을 보내 서비스를 호출합니다. 각 요청은 수행할 작업, 작업 대상(예: 비즈니스 객체) 및 매개변수를 지정합니다.
  • 레벨 1 – 레벨 1 API는 리소스 개념을 지원합니다. 리소스에 대한 작업을 수행하려면 클라이언트가 수행할 작업과 매개변수를 지정하는 POST 요청을 만듭니다.
  • 레벨 2 – 레벨 2 API는 HTTP 동사를 사용하여 작업을 수행합니다. GET은 검색을 위해, POST는 생성을 위해, PUT은 업데이트하기 위해 사용합니다. 요청 쿼리 매개변수와 본문(있는 경우)은 작업의 매개변수를 지정합니다. 이를 통해 서비스는 GET 요청에 대한 캐싱과 같은 웹 인프라를 활용할 수 있습니다.
  • 레벨 3 – 레벨 3 API의 디자인은 HATEOAS(하이퍼텍스트를 애플리케이션 상태 엔진으로 사용)라는 끔찍한 이름의 원칙에 기반합니다. 기본적인 아이디어는 GET 요청으로 반환된 리소스의 표현에 해당 리소스에 허용되는 작업을 수행하기 위한 링크가 포함된다는 것입니다. 예를 들어, 클라이언트는 주문을 검색하기 위해 전송된 GET 요청에 대한 응답으로 반환된 주문 표현의 링크를 사용하여 주문을 취소할 수 있습니다. HATEOAS의 이점은 더 이상 클라이언트 코드에 URL을 하드와이어링할 필요가 없다는 것입니다. 또 다른 이점은 리소스의 표현에 허용되는 작업에 대한 링크가 포함되어 있기 때문에 클라이언트가 현재 상태에서 리소스에 대해 수행할 수 있는 작업이 무엇인지 추측할 필요가 없다는 것입니다.

HTTP 기반 프로토콜을 사용하는 데는 수많은 이점이 있습니다.

  • HTTP는 간단하고 익숙합니다.
  • Postman 과 같은 확장 프로그램을 사용하거나 명령줄에서 curl을 사용하여 브라우저에서 HTTP API를 테스트할 수 있습니다(JSON이나 다른 텍스트 형식이 사용된다고 가정).
  • 요청/응답 방식의 커뮤니케이션을 직접 지원합니다.
  • 물론 HTTP는 방화벽에 친화적입니다.
  • 중간 브로커가 필요 없으므로 시스템 아키텍처가 간소화됩니다.

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

  • 요청/응답 스타일의 상호작용만 직접적으로 지원합니다. 알림에는 HTTP를 사용할 수 있지만 서버는 항상 HTTP 응답을 보내야 합니다.
  • 클라이언트와 서비스가 (메시지를 버퍼링하는 중개자 없이) 직접 통신하므로, 교환이 이루어지는 동안 둘 다 실행 중이어야 합니다.
  • 클라이언트는 각 서비스 인스턴스의 위치(즉, URL)를 알아야 합니다. API Gateway에 관한 이전 문서 에서 설명한 대로 이는 현대 애플리케이션에서 사소한 문제가 아닙니다. 클라이언트는 서비스 검색 메커니즘을 사용하여 서비스 인스턴스를 찾아야 합니다.

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

절약

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

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

Thrift는 다양한 메시지 형식을 지원합니다: JSON, 바이너리, 컴팩트 바이너리. 바이너리는 디코딩 속도가 JSON보다 빠르기 때문에 더 효율적입니다. 이름에서 알 수 있듯이 컴팩트 바이너리는 공간 효율적인 형식입니다. 물론 JSON은 사람과 브라우저 모두에게 친화적입니다. Thrift를 사용하면 원시 TCP와 HTTP를 비롯한 다양한 전송 프로토콜을 선택할 수 있습니다. 원시 TCP는 HTTP보다 더 효율적일 가능성이 높습니다. 그러나 HTTP는 방화벽, 브라우저, 사람에게 친화적입니다.

메시지 형식

이제 HTTP와 Thrift를 살펴보았으니 메시지 형식 문제를 살펴보겠습니다. 메시징 시스템이나 REST를 사용하는 경우 메시지 형식을 선택할 수 있습니다. Thrift와 같은 다른 IPC 메커니즘은 소수의 메시지 형식, 아마도 단 하나만 지원할 수도 있습니다. 어느 경우든, 여러 언어의 메시지 형식을 사용하는 것이 중요합니다. 오늘날 단일 언어로 마이크로서비스를 작성하고 있다 하더라도 앞으로는 다른 언어를 사용하게 될 가능성이 높습니다.

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

XML 문서의 구조는 XML 스키마 에 의해 지정됩니다. 시간이 지나면서 개발자 커뮤니티는 JSON에도 비슷한 메커니즘이 필요하다는 것을 깨닫게 되었습니다. 한 가지 옵션은 JSON 스키마를 단독으로 사용하거나 Swagger와 같은 IDL의 일부로 사용하는 것입니다.

텍스트 기반 메시지 형식을 사용하는 것의 단점은 메시지가 장황해지는 경향이 있다는 점입니다. 특히 XML의 경우 더욱 그렇습니다. 메시지는 자체 설명적이므로 각 메시지에는 속성 값 외에도 속성 이름이 포함됩니다. 또 다른 단점은 텍스트 구문 분석의 오버헤드입니다. 따라서 이진 형식을 사용하는 것을 고려해 보는 것이 좋습니다.

선택할 수 있는 이진 형식이 여러 가지 있습니다. Thrift RPC를 사용하는 경우 바이너리 Thrift를 사용할 수 있습니다. 메시지 형식을 선택할 수 있는 경우 인기 있는 옵션으로는 Protocol BuffersApache Avro 가 있습니다. 두 가지 형식 모두 메시지 구조를 정의하는 데 필요한 형식화된 IDL을 제공합니다. 그러나 한 가지 차이점은 Protocol Buffers는 태그가 지정된 필드를 사용하는 반면 Avro 소비자는 메시지를 해석하기 위해 스키마를 알아야 한다는 것입니다. 결과적으로 Protocol Buffers를 사용하면 Avro보다 API 진화가 더 쉽습니다. 이 블로그 게시물은 Thrift, Protocol Buffers, Avro를 훌륭하게 비교하고 있습니다.

요약

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

편집자 – 이 7부작 시리즈 기사는 이제 완료되었습니다.

  1. 마이크로서비스 소개
  2. 마이크로서비스 구축: API 게이트웨이 사용
  3. 마이크로서비스 구축: 마이크로서비스 아키텍처에서의 프로세스 간 통신(본 문서)
  4. 마이크로서비스 아키텍처에서의 서비스 발견
  5. 마이크로서비스를 위한 이벤트 기반 데이터 관리
  6. 마이크로서비스 배포 전략 선택
  7. 모놀리스를 마이크로서비스로 리팩토링

NGINX Plus를 사용하여 마이크로서비스를 구현하는 것에 대한 정보와 더불어 전체 문서 세트를 전자책으로 다운로드할 수도 있습니다. – 마이크로서비스: 설계부터 배포까지 .

게스트 블로거 크리스 리처드슨은 Amazon EC2를 위한 초기 Java PaaS(서비스형 플랫폼)인 CloudFoundry.com 의 창립자입니다. 그는 현재 기업들과 협의해 애플리케이션을 개발하고 배포하는 방법을 개선하기 위해 노력하고 있습니다. 그는 또한 http://microservices.io 에서 마이크로서비스에 관해 정기적으로 블로그를 운영하고 있습니다.


"이 블로그 게시물에는 더 이상 사용할 수 없거나 더 이상 지원되지 않는 제품이 참조될 수 있습니다. 사용 가능한 F5 NGINX 제품과 솔루션에 대한 최신 정보를 보려면 NGINX 제품군을 살펴보세요. NGINX는 이제 F5의 일부가 되었습니다. 이전의 모든 NGINX.com 링크는 F5.com의 유사한 NGINX 콘텐츠로 리디렉션됩니다."