다음을 통해 공유


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

팁 (조언)

이 콘텐츠는 .NET Docs 또는 오프라인으로 읽을 수 있는 다운로드 가능한 무료 PDF로 제공되는 컨테이너화된 .NET 애플리케이션용 .NET 마이크로 서비스 아키텍처인 eBook에서 발췌한 내용입니다.

컨테이너화된 .NET 애플리케이션을 위한 .NET 마이크로서비스 아키텍처 eBook의 표지 썸네일.

단일 프로세스에서 실행되는 모놀리식 애플리케이션에서 구성 요소는 언어 수준 메서드 또는 함수 호출을 사용하여 서로 호출합니다. 코드(예 new ClassName(): )를 사용하여 개체를 만드는 경우 강력하게 결합하거나 구체적인 개체 인스턴스가 아닌 추상화를 참조하여 종속성 주입을 사용하는 경우 분리된 방식으로 호출할 수 있습니다. 어느 쪽이든 개체는 동일한 프로세스 내에서 실행됩니다. 모놀리식 애플리케이션에서 마이크로 서비스 기반 애플리케이션으로 변경할 때 가장 큰 문제는 통신 메커니즘을 변경하는 것입니다. In-Process 메서드 호출에서 서비스에 대한 RPC 호출로 직접 변환하면 분산 환경에서 잘 수행되지 않는 수다스럽고 효율적이지 않은 통신이 발생합니다. 분산 시스템을 제대로 디자인하는 문제는 개발자가 모놀리식에서 분산 디자인으로 이동할 때 자주 하는 가정을 나열 하는 분산 컴퓨팅의 오류 라고도 하는 캐논이 있다는 것을 잘 알고 있습니다.

하나의 솔루션은 없지만 여러 가지가 있습니다. 한 가지 솔루션에는 비즈니스 마이크로 서비스를 최대한 격리하는 작업이 포함됩니다. 그런 다음, 내부 마이크로 서비스 간의 비동기 통신을 사용하고, 객체 간 프로세스 내에서 일반적인 세밀한 통신을 보다 구체적이지 않은 통신으로 대체합니다. 호출을 그룹화하고 여러 내부 호출의 결과를 집계하는 데이터를 클라이언트에 반환하여 이 작업을 수행할 수 있습니다.

마이크로 서비스 기반 애플리케이션은 일반적으로 여러 서버 또는 호스트에서 여러 프로세스 또는 서비스에서 실행되는 분산 시스템입니다. 각 서비스 인스턴스는 일반적으로 프로세스입니다. 따라서 서비스는 각 서비스의 특성에 따라 HTTP, AMQP 또는 TCP와 같은 이진 프로토콜과 같은 프로세스 간 통신 프로토콜을 사용하여 상호 작용해야 합니다.

마이크로 서비스 커뮤니티는 "스마트 엔드포인트 및 멍청한 파이프"의 철학을 홍보합니다. 이 슬로건은 마이크로 서비스 간에 가능한 한 분리되고 단일 마이크로 서비스 내에서 가능한 한 응집력 있는 디자인을 장려합니다. 앞에서 설명한 대로 각 마이크로 서비스는 자체 데이터와 자체 도메인 논리를 소유합니다. 그러나 엔드 투 엔드 애플리케이션을 구성하는 마이크로 서비스는 일반적으로 중앙 집중식 비즈니스 프로세스 오케스트레이터 대신 WS-* 및 유연한 이벤트 기반 통신과 같은 복잡한 프로토콜이 아닌 REST 통신을 사용하여 간단하게 작성됩니다.

일반적으로 사용되는 두 프로토콜은 리소스 API를 사용한 HTTP 요청/응답(대부분 쿼리 시) 및 여러 마이크로 서비스에서 업데이트를 통신할 때 간단한 비동기 메시징입니다. 이러한 내용은 다음 섹션에서 자세히 설명합니다.

통신 유형

클라이언트와 서비스는 서로 다른 시나리오와 목표를 대상으로 하는 다양한 유형의 통신을 통해 통신할 수 있습니다. 처음에는 이러한 유형의 통신을 두 축으로 분류할 수 있습니다.

첫 번째 축은 프로토콜이 동기 또는 비동기인지를 정의합니다.

  • 동기 프로토콜입니다. HTTP는 동기 프로토콜입니다. 클라이언트는 요청을 보내고 서비스에서 응답을 기다립니다. 이는 동기(스레드가 차단됨) 또는 비동기(스레드가 차단되지 않고 응답이 결국 콜백에 도달할 수 있음)일 수 있는 클라이언트 코드 실행과는 독립적입니다. 여기서 중요한 점은 프로토콜(HTTP/HTTPS)이 동기적이며 클라이언트 코드는 HTTP 서버 응답을 받을 때만 해당 작업을 계속할 수 있다는 것입니다.

  • 비동기 프로토콜입니다. AMQP(많은 운영 체제 및 클라우드 환경에서 지원하는 프로토콜)와 같은 다른 프로토콜은 비동기 메시지를 사용합니다. 클라이언트 코드 또는 메시지 발신자는 일반적으로 응답을 기다리지 않습니다. RabbitMQ 큐 또는 다른 메시지 브로커에 메시지를 보낼 때와 같이 메시지를 보냅니다.

두 번째 축은 통신에 단일 수신기 또는 여러 수신기가 있는지를 정의합니다.

  • 단일 수신기. 각 요청은 정확히 하나의 수신기 또는 서비스에서 처리해야 합니다. 이 통신의 예는 명령 패턴입니다.

  • 여러 수신기. 각 요청은 0부터 여러 수신기까지 처리할 수 있습니다. 이러한 유형의 통신은 비동기적이어야 합니다. 예를 들어 이벤트 기반 아키텍처와 같은 패턴에 사용되는 게시/구독 메커니즘이 있습니다. 이벤트를 통해 여러 마이크로 서비스 간에 데이터 업데이트를 전파할 때 이벤트 버스 인터페이스 또는 메시지 브로커를 기반으로 합니다. 일반적으로 토픽 및 구독을 사용하여 Service Bus 또는 Azure Service Bus와 같은 유사한 아티팩트를 통해 구현됩니다.

마이크로 서비스 기반 애플리케이션은 종종 이러한 통신 스타일의 조합을 사용합니다. 가장 일반적인 유형은 일반 Web API HTTP 서비스를 호출할 때 HTTP/HTTPS와 같은 동기 프로토콜을 사용하는 단일 수신기 통신입니다. 마이크로 서비스는 일반적으로 마이크로 서비스 간의 비동기 통신에 메시징 프로토콜을 사용합니다.

이러한 축에 대해 알고 이해하면 가능한 통신 메커니즘의 명확성을 제공할 수 있지만, 마이크로 서비스를 구축할 때는 중요한 문제가 아닙니다. 클라이언트 스레드 실행의 비동기 특성이나 선택한 프로토콜의 비동기 특성은 마이크로 서비스를 통합할 때 중요한 요소가 아닙니다. 중요한 것은 다음 섹션에 설명된 대로 마이크로 서비스의 독립성을 유지하면서 마이크로 서비스를 비동기적으로 통합할 수 있다는 것입니다.

비동기 마이크로 서비스 통합은 마이크로 서비스의 자율성을 적용합니다.

언급했듯이 마이크로 서비스 기반 애플리케이션을 빌드할 때 중요한 점은 마이크로 서비스를 통합하는 방법입니다. 이상적으로는 내부 마이크로 서비스 간의 통신을 최소화해야 합니다. 마이크로 서비스 간의 통신이 적을수록 좋습니다. 그러나 대부분의 경우 어떻게든 마이크로 서비스를 통합해야 합니다. 이렇게 해야 하는 경우 여기서 중요한 규칙은 마이크로 서비스 간의 통신이 비동기적이어야 한다는 것입니다. 그렇다고 해서 특정 프로토콜(예: 비동기 메시징 및 동기 HTTP)을 사용해야 한다는 의미는 아닙니다. 즉, 마이크로 서비스 간 통신은 데이터를 비동기적으로 전파해야 하지만 초기 서비스의 HTTP 요청/응답 작업의 일부로 다른 내부 마이크로 서비스에 의존하지 않도록 해야 합니다.

가능하면 쿼리가 아닌 여러 마이크로 서비스 간의 동기 통신(요청/응답)에 의존하지 마세요. 각 마이크로 서비스의 목표는 종단 간 애플리케이션의 일부인 다른 서비스가 다운되거나 비정상인 경우에도 클라이언트 소비자가 자율적이고 사용할 수 있도록 하는 것입니다. 클라이언트 애플리케이션에 대한 응답을 제공할 수 있도록 한 마이크로 서비스에서 다른 마이크로 서비스(예: 데이터 쿼리에 대한 HTTP 요청 수행)로 호출해야 하는 경우 일부 마이크로 서비스가 실패할 때 복원력이 없는 아키텍처가 있습니다.

또한 그림 4-15의 첫 번째 부분에 표시된 것처럼 HTTP 요청 체인을 사용하여 긴 요청/응답 주기를 만들 때와 같이 마이크로 서비스 간에 HTTP 종속성이 있으면 마이크로 서비스가 자율적이지 않을 뿐만 아니라 해당 체인의 서비스 중 하나가 잘 수행되지 않는 즉시 성능에 영향을 줍니다.

쿼리 요청과 같은 마이크로 서비스 간에 동기 종속성을 더 많이 추가할수록 클라이언트 앱에 대한 전반적인 응답 시간이 더 나빠집니다.

마이크로 서비스에서 세 가지 유형의 통신을 보여 주는 다이어그램

그림 4-15. 마이크로 서비스 간 통신의 안티 패턴 및 패턴

위의 다이어그램에 표시된 것처럼 동기 통신에서 클라이언트 요청을 제공하는 동안 마이크로 서비스 간에 요청의 "체인"이 생성됩니다. 이것은 안티 패턴입니다. 비동기 통신 마이크로 서비스에서는 비동기 메시지 또는 http 폴링을 사용하여 다른 마이크로 서비스와 통신하지만 클라이언트 요청은 즉시 제공됩니다.

마이크로 서비스가 다른 마이크로 서비스에서 추가 작업을 발생시켜야 하는 경우 가능하면 원래 마이크로 서비스 요청 및 회신 작업의 일부로 동기적으로 해당 작업을 수행하지 마세요. 대신 비동기 메시징 또는 통합 이벤트, 큐 등을 사용하여 비동기적으로 수행합니다. 그러나 가능한 한 원래 동기 요청 및 회신 작업의 일부로 동작을 동기적으로 호출하지 마세요.

마지막으로(그리고 마이크로 서비스를 빌드할 때 대부분의 문제가 발생하는 경우), 초기 마이크로 서비스에 원래 다른 마이크로 서비스가 소유한 데이터가 필요한 경우 해당 데이터에 대한 동기 요청을 수행하지 마세요. 대신 최종 일관성(일반적으로 향후 섹션에서 설명한 대로 통합 이벤트 사용)을 사용하여 해당 데이터(필요한 특성만)를 초기 서비스의 데이터베이스에 복제하거나 전파합니다.

각 마이크로 서비스 섹션에 대한 도메인 모델 경계 식별의 앞부분에서 설명한 것처럼, 여러 마이크로 서비스에서 일부 데이터를 복제하는 것은 잘못된 디자인이 아닙니다. 반대로 이 작업을 수행하는 경우 데이터를 해당 추가 도메인 또는 제한된 컨텍스트의 특정 언어 또는 용어로 변환할 수 있습니다. 예를 들어 eShopOnContainers 애플리케이션에는 이름이 identity-api지정된 User 엔터티를 사용하여 대부분의 사용자 데이터를 담당하는 마이크로 서비스가 있습니다. 그러나 사용자에 대한 데이터를 마이크로 서비스 내에 Ordering 저장해야 하는 경우 해당 데이터를 명명 Buyer된 다른 엔터티로 저장합니다. Buyer 엔터티는 원래 User 엔터티와 동일한 ID를 공유하지만 전체 사용자 프로필이 아닌 도메인에 Ordering 필요한 몇 가지 특성만 있을 수 있습니다.

최종 일관성을 갖기 위해 모든 프로토콜을 사용하여 마이크로 서비스에서 데이터를 비동기적으로 통신하고 전파할 수 있습니다. 앞에서 설명한 대로 이벤트 버스 또는 메시지 브로커를 사용하여 통합 이벤트를 사용하거나 다른 서비스를 폴링하여 HTTP를 사용할 수도 있습니다. 이는 중요하지 않습니다. 중요한 규칙은 마이크로 서비스 간에 동기 종속성을 만들지 않는 것입니다.

다음 섹션에서는 마이크로 서비스 기반 애플리케이션에서 사용할 수 있는 여러 통신 스타일을 설명합니다.

통신 스타일

사용하려는 통신 유형에 따라 통신에 사용할 수 있는 많은 프로토콜 및 선택 사항이 있습니다. 동기 요청/응답 기반 통신 메커니즘을 사용하는 경우 특히 Docker 호스트 또는 마이크로 서비스 클러스터 외부에 서비스를 게시하는 경우 HTTP 및 REST 접근 방식과 같은 프로토콜이 가장 일반적입니다. Docker 호스트 또는 마이크로 서비스 클러스터 내에서 내부적으로 서비스 간에 통신하는 경우 이진 형식 통신 메커니즘(예: TCP 및 이진 형식을 사용하는 WCF)을 사용할 수도 있습니다. 또는 AMQP와 같은 비동기 메시지 기반 통신 메커니즘을 사용할 수 있습니다.

JSON 또는 XML과 같은 여러 메시지 형식 또는 더 효율적일 수 있는 이진 형식도 있습니다. 선택한 이진 형식이 표준이 아닌 경우 해당 형식을 사용하여 서비스를 공개적으로 게시하는 것은 좋지 않을 수 있습니다. 마이크로 서비스 간의 내부 통신에 비표준 형식을 사용할 수 있습니다. Docker 호스트 또는 마이크로 서비스 클러스터 내의 마이크로 서비스(예: Docker 오케스트레이터) 또는 마이크로 서비스와 통신하는 독점 클라이언트 애플리케이션 간에 통신할 때 이 작업을 수행할 수 있습니다.

HTTP 및 REST와의 요청/응답 통신

클라이언트가 요청/응답 통신을 사용하는 경우 서비스에 요청을 보낸 다음, 서비스는 요청을 처리하고 응답을 다시 보냅니다. 요청/응답 통신은 클라이언트 앱에서 실시간 UI(라이브 사용자 인터페이스)에 대한 데이터를 쿼리하는 데 특히 적합합니다. 따라서 마이크로 서비스 아키텍처에서는 그림 4-16과 같이 대부분의 쿼리에 이 통신 메커니즘을 사용할 수 있습니다.

라이브 쿼리 및 업데이트에 대한 요청/응답 통신을 보여 주는 다이어그램

그림 4-16. HTTP 요청/응답 통신 사용(동기 또는 비동기)

클라이언트가 요청/응답 통신을 사용하는 경우 응답이 짧은 시간, 일반적으로 1초 미만 또는 최대 몇 초 안에 도착한다고 가정합니다. 지연된 응답의 경우 메시징 패턴메시징 기술을 기반으로 비동기 통신을 구현해야 합니다. 이는 다음 섹션에서 설명하는 다른 접근 방식입니다.

요청/응답 통신에 널리 사용되는 아키텍처 스타일은 REST입니다. 이 방법은 GET, POST 및 PUT과 같은 HTTP 동사를 수용하는 HTTP 프로토콜을 기반으로 하며 긴밀하게 결합됩니다. REST는 서비스를 만들 때 가장 일반적으로 사용되는 아키텍처 통신 방법입니다. ASP.NET Core Web API 서비스를 개발할 때 REST 서비스를 구현할 수 있습니다.

HTTP REST 서비스를 인터페이스 정의 언어로 사용하는 경우 추가 값이 있습니다. 예를 들어 Swagger 메타데이터 를 사용하여 서비스 API를 설명하는 경우 서비스를 직접 검색하고 사용할 수 있는 클라이언트 스텁을 생성하는 도구를 사용할 수 있습니다.

추가 리소스

HTTP 기반 푸시 및 실시간 통신

또 다른 가능성(일반적으로 REST와 다른 용도로)은 ASP.NET SignalRWebSockets와 같은 프로토콜과 같은 상위 수준 프레임워크와의 실시간 및 일대다 통신입니다.

그림 4-17에서 볼 수 있듯이 실시간 HTTP 통신은 서버가 클라이언트가 새 데이터를 요청할 때까지 기다리지 않고 데이터를 사용할 수 있게 되면 연결된 클라이언트에 콘텐츠를 푸시하는 서버 코드를 가질 수 있음을 의미합니다.

SignalR 기반 푸시 및 실시간 통신을 보여 주는 다이어그램

그림 4-17. 실시간 일대다 비동기적 메시지 통신

SignalR은 백 엔드 서버에서 클라이언트로 콘텐츠를 푸시하기 위한 실시간 통신을 달성하는 좋은 방법입니다. 통신은 실시간으로 수행되므로 클라이언트 앱은 거의 즉시 변경 내용을 표시합니다. 일반적으로 많은 WebSockets 연결(클라이언트당 하나)을 사용하여 WebSockets와 같은 프로토콜에 의해 처리됩니다. 일반적인 예는 서비스가 스포츠 게임의 점수 변경을 여러 클라이언트 웹앱에 동시에 전달하는 경우입니다.