팁 (조언)
이 콘텐츠는 .NET Docs 또는 오프라인으로 읽을 수 있는 다운로드 가능한 무료 PDF로 제공되는 컨테이너화된 .NET 애플리케이션용 .NET 마이크로 서비스 아키텍처인 eBook에서 발췌한 내용입니다.
챌린지 #1: 각 마이크로 서비스의 경계를 정의하는 방법
마이크로 서비스 경계를 정의하는 것은 누구나 직면하는 첫 번째 과제일 것입니다. 각 마이크로 서비스는 애플리케이션의 일부여야 하며 각 마이크로 서비스는 전달되는 모든 이점과 과제와 함께 자율적이어야 합니다. 그러나 이러한 경계를 어떻게 식별할 수 있을까요?
먼저 애플리케이션의 논리 도메인 모델 및 관련 데이터에 집중해야 합니다. 동일한 애플리케이션 내에서 분리된 데이터 섬과 다양한 컨텍스트를 식별해 봅니다. 각 컨텍스트에는 다른 비즈니스 언어(다른 비즈니스 용어)가 있을 수 있습니다. 컨텍스트를 독립적으로 정의하고 관리해야 합니다. 이러한 다양한 컨텍스트에서 사용되는 용어와 엔터티는 비슷하게 들릴 수 있지만 특정 컨텍스트에서 비즈니스 개념이 다른 컨텍스트에서 다른 용도로 사용되고 다른 이름을 가질 수도 있다는 것을 알 수 있습니다. 예를 들어 사용자는 ID 또는 멤버 자격 컨텍스트의 사용자, CRM 컨텍스트의 고객, 주문 컨텍스트의 구매자 등으로 참조할 수 있습니다.
각 컨텍스트에 대해 서로 다른 도메인을 사용하여 여러 애플리케이션 컨텍스트 간의 경계를 식별하는 방법은 각 비즈니스 마이크로 서비스 및 관련 도메인 모델 및 데이터에 대한 경계를 정확히 식별하는 방법입니다. 항상 이러한 마이크로 서비스 간의 결합을 최소화하려고 합니다. 이 가이드에서는 나중에 각 마이크로 서비스에 대한 도메인 모델 경계 식별 섹션에서 이 식별 및 도메인 모델 디자인에 대해 자세히 설명합니다.
과제 #2: 여러 마이크로 서비스에서 데이터를 검색하는 쿼리를 만드는 방법
두 번째 과제는 원격 클라이언트 앱에서 마이크로 서비스에 대한 수다스러운 통신을 피하면서 여러 마이크로 서비스에서 데이터를 검색하는 쿼리를 구현하는 방법입니다. 예를 들어 장바구니, 카탈로그 및 사용자 ID 마이크로 서비스가 소유한 사용자 정보를 표시해야 하는 모바일 앱의 단일 화면이 있습니다. 또 다른 예로 여러 마이크로 서비스에 있는 많은 테이블이 포함된 복잡한 보고서가 있습니다. 올바른 솔루션은 쿼리의 복잡성에 따라 달라집니다. 그러나 시스템의 통신 효율성을 향상하려면 정보를 집계하는 방법이 필요합니다. 가장 인기 있는 솔루션은 다음과 같습니다.
API 게이트웨이. 서로 다른 데이터베이스를 소유하는 여러 마이크로 서비스의 간단한 데이터 집계의 경우 권장되는 방법은 API 게이트웨이라고 하는 집계 마이크로 서비스입니다. 그러나 시스템에서 초크 포인트가 될 수 있고 마이크로 서비스 자율성의 원칙을 위반할 수 있으므로 이 패턴을 구현하는 데 주의해야 합니다. 이러한 가능성을 완화하기 위해 시스템의 수직 "조각" 또는 비즈니스 영역에 초점을 맞춘 여러 개의 세로로 세분화된 API 게이트웨이를 가질 수 있습니다. API 게이트웨이 패턴은 나중에 API 게이트웨이 섹션에서 자세히 설명합니다.
GraphQL 페더레이션 마이크로 서비스가 이미 GraphQL을 사용하고 있는지 고려하는 한 가지 옵션은 GraphQL 페더레이션입니다. 페더레이션을 사용하면 다른 서비스의 "하위 그래프"를 정의하고 독립 실행형 스키마 역할을 하는 집계 "슈퍼 그래프"로 구성할 수 있습니다.
쿼리/읽기 테이블이 있는 CQRS입니다. 여러 마이크로 서비스의 데이터를 집계하는 또 다른 솔루션은 구체화된 뷰 패턴입니다. 이 방법에서는 여러 마이크로 서비스가 소유한 데이터를 사용하여 읽기 전용 테이블을 미리 생성합니다(실제 쿼리가 발생하기 전에 비정규화된 데이터 준비). 테이블에는 클라이언트 앱의 요구에 적합한 형식이 있습니다.
모바일 앱의 화면과 같은 것을 고려합니다. 단일 데이터베이스가 있는 경우 여러 테이블과 관련된 복잡한 조인을 수행하는 SQL 쿼리를 사용하여 해당 화면의 데이터를 함께 끌어올 수 있습니다. 그러나 여러 데이터베이스가 있고 각 데이터베이스가 다른 마이크로 서비스에서 소유하는 경우 해당 데이터베이스를 쿼리하고 SQL 조인을 만들 수 없습니다. 복잡한 쿼리는 문제가 됩니다. CQRS 접근 방식을 사용하여 요구 사항을 해결할 수 있습니다. 쿼리에만 사용되는 다른 데이터베이스에 비정규화된 테이블을 만듭니다. 이 테이블은 애플리케이션의 화면에 필요한 필드와 쿼리 테이블의 열 간에 일대일 관계를 사용하여 복잡한 쿼리에 필요한 데이터를 위해 특별히 디자인할 수 있습니다. 또한 보고 목적으로 사용될 수 있습니다.
이 방법은 원래 문제(마이크로 서비스에서 쿼리 및 조인하는 방법)를 해결할 뿐만 아니라 애플리케이션에 필요한 데이터가 쿼리 테이블에 이미 있으므로 복잡한 조인과 비교할 때 성능이 상당히 향상됩니다. 물론 쿼리/읽기 테이블과 함께 CQRS(명령 및 쿼리 책임 분리)를 사용하는 것은 추가 개발 작업을 의미하며 최종 일관성을 수용해야 합니다. 그럼에도 불구하고 협업 시나리오 (또는 관점에 따라 경쟁 시나리오)의 성능 및 높은 확장성에 대한 요구 사항은 여러 데이터베이스가 있는 CQRS를 적용해야 하는 위치입니다.
중앙 데이터베이스의 "콜드 데이터". 실시간 데이터가 필요하지 않을 수 있는 복잡한 보고서 및 쿼리의 경우 일반적인 방법은 "핫 데이터"(마이크로 서비스의 트랜잭션 데이터)를 보고에만 사용되는 대규모 데이터베이스로 내보내는 것입니다. 해당 중앙 데이터베이스 시스템은 Hadoop과 같은 빅 데이터 기반 시스템이 될 수 있습니다. Azure SQL Data Warehouse 기반 데이터 웨어하우스와 같은 데이터 웨어하우스 또는 보고서에만 사용되는 단일 SQL 데이터베이스도 있습니다(크기가 문제가 되지 않는 경우).
이 중앙 집중식 데이터베이스는 실시간 데이터가 필요하지 않은 쿼리 및 보고서에만 사용됩니다. 원본 업데이트 및 트랜잭션은 진실의 원본으로 마이크로 서비스 데이터에 있어야 합니다. 데이터를 동기화하는 방법은 이벤트 기반 통신(다음 섹션에서 설명)을 사용하거나 다른 데이터베이스 인프라 가져오기/내보내기 도구를 사용하는 것입니다. 이벤트 기반 통신을 사용하는 경우 해당 통합 프로세스는 CQRS 쿼리 테이블에 대해 앞에서 설명한 대로 데이터를 전파하는 방식과 유사합니다.
그러나 애플리케이션 디자인이 복잡한 쿼리를 위해 여러 마이크로 서비스의 정보를 지속적으로 집계하는 경우 마이크로 서비스가 다른 마이크로 서비스에서 가능한 한 격리되어야 -a 잘못된 디자인의 증상일 수 있습니다. (콜드 데이터 중앙 데이터베이스를 항상 사용해야 하는 보고서/분석은 제외됩니다.) 이 문제가 종종 마이크로 서비스를 병합하는 이유일 수 있습니다. 각 마이크로 서비스의 진화 및 배포의 자율성과 강력한 종속성, 응집력 및 데이터 집계의 균형을 유지해야 합니다.
과제 #3: 여러 마이크로 서비스에서 일관성을 달성하는 방법
앞에서 설명한 것처럼 각 마이크로 서비스가 소유한 데이터는 해당 마이크로 서비스에 비공개이며 해당 마이크로 서비스 API를 사용하여서만 액세스할 수 있습니다. 따라서 여러 마이크로 서비스에서 일관성을 유지하면서 엔드 투 엔드 비즈니스 프로세스를 구현하는 방법이 제시된 과제입니다.
이 문제를 분석하기 위해 eShopOnContainers 참조 애플리케이션의 예제를 살펴보겠습니다. 카탈로그 마이크로 서비스는 제품 가격을 포함하여 모든 제품에 대한 정보를 유지 관리합니다. 장바구니 마이크로 서비스는 사용자가 장바구니에 추가하는 제품 항목에 대한 임시 데이터를 관리하며, 여기에는 장바구니에 추가된 항목의 가격이 포함됩니다. 카탈로그에서 제품의 가격이 업데이트되면 해당 가격이 동일한 제품을 보유하는 활성 바구니에서도 업데이트되어야 하며, 시스템은 특정 항목의 가격이 바구니에 추가된 이후 변경되었음을 사용자에게 경고해야 합니다.
이 애플리케이션의 가상 모놀리식 버전에서 제품 테이블의 가격이 변경되면 카탈로그 하위 시스템은 ACID 트랜잭션을 사용하여 Basket 테이블의 현재 가격을 업데이트할 수 있습니다.
그러나 마이크로 서비스 기반 애플리케이션에서 Product 및 Basket 테이블은 해당 마이크로 서비스에서 소유합니다. 그림 4-9에 표시된 것처럼 직접 쿼리에서도 다른 마이크로 서비스가 소유한 테이블/스토리지를 자체 트랜잭션에 포함해서는 안 됩니다.
그림 4-9. 마이크로 서비스는 다른 마이크로 서비스의 테이블에 직접 액세스할 수 없습니다.
Basket 테이블은 Basket 마이크로 서비스에서 소유하므로 Catalog 마이크로 서비스는 Basket 테이블을 직접 업데이트해서는 안 됩니다. Basket 마이크로 서비스를 업데이트하려면, 카탈로그 마이크로 서비스는 비동기 통신(예: 메시지 및 이벤트 기반 통합 이벤트)을 활용하여 최종적 일관성을 도입하는 것이 바람직합니다. 이것이 eShopOnContainers 참조 애플리케이션이 마이크로 서비스에서 이러한 유형의 일관성을 수행하는 방법입니다.
CAP 정리에서 설명한 대로 가용성과 ACID 강력한 일관성 중에서 선택해야 합니다. 대부분의 마이크로 서비스 기반 시나리오는 강력한 일관성과 달리 가용성과 높은 확장성을 요구합니다. 중요 업무용 애플리케이션은 계속 실행되어야 하며 개발자는 강력한 일관성의 문제를 해결하기 위해 약하거나 궁극적인 일관성 기술을 사용할 수 있습니다. 이는 대부분의 마이크로 서비스 기반 아키텍처에서 사용하는 접근 방식입니다.
또한 ACID 스타일 또는 2단계 커밋 트랜잭션은 마이크로 서비스 원칙에만 적용되는 것이 아닙니다. 대부분의 NoSQL 데이터베이스(예: Azure Cosmos DB, MongoDB 등)는 분산 데이터베이스 시나리오에서 일반적으로 2단계 커밋 트랜잭션을 지원하지 않습니다. 그러나 서비스 및 데이터베이스 간에 데이터 일관성을 유지하는 것은 필수적입니다. 이 문제는 특정 데이터가 중복되어야 하는 경우(예: 카탈로그 마이크로 서비스 및 Basket 마이크로 서비스에 제품의 이름 또는 설명이 있어야 하는 경우) 여러 마이크로 서비스에 변경 내용을 전파하는 방법에 대한 질문과도 관련이 있습니다.
이 문제에 대한 좋은 해결 방법은 이벤트 기반 통신과 게시 및 구독 시스템을 통해 명시되는 마이크로 서비스 간에 최종 일관성을 사용하는 것입니다. 이러한 항목은 이 가이드의 뒷부분에 있는 비동기 이벤트 기반 통신 섹션에서 설명합니다.
과제 #4: 마이크로 서비스 경계를 넘어 통신을 디자인하는 방법
마이크로 서비스 경계를 넘어 통신하는 것은 정말 어려운 일입니다. 이 컨텍스트에서 통신은 사용해야 하는 프로토콜(HTTP 및 REST, AMQP, 메시징 등)을 참조하지 않습니다. 대신 사용해야 하는 통신 스타일, 특히 마이크로 서비스가 얼마나 결합되어야 하는지를 다룹니다. 결합 수준에 따라 오류가 발생할 때 해당 오류가 시스템에 미치는 영향은 크게 달라집니다.
마이크로 서비스 기반 애플리케이션과 같은 분산 시스템에서는 많은 아티팩트가 이동하고 많은 서버 또는 호스트에서 분산 서비스를 통해 구성 요소가 결국 실패합니다. 부분 오류 및 더 큰 중단이 발생하므로 이러한 유형의 분산 시스템의 일반적인 위험을 고려하여 마이크로 서비스 및 통신을 설계해야 합니다.
널리 사용되는 방법은 단순성으로 인해 HTTP(REST) 기반 마이크로 서비스를 구현하는 것입니다. HTTP 기반 접근 방식은 완벽하게 허용됩니다. 여기서 문제는 사용하는 방법과 관련이 있습니다. HTTP 요청 및 응답을 사용하여 클라이언트 애플리케이션 또는 API 게이트웨이에서 마이크로 서비스와 상호 작용하는 경우 괜찮습니다. 그러나 마이크로 서비스에서 동기 HTTP 호출의 긴 체인을 만들어 마이크로 서비스가 모놀리식 애플리케이션의 개체인 것처럼 경계를 넘어 통신하는 경우 결국 애플리케이션에 문제가 발생합니다.
예를 들어 클라이언트 애플리케이션이 주문 마이크로 서비스와 같은 개별 마이크로 서비스에 대한 HTTP API 호출을 수행한다고 상상해 보십시오. 순서 지정 마이크로 서비스가 동일한 요청/응답 주기 내에서 HTTP를 사용하여 추가 마이크로 서비스를 호출하는 경우 HTTP 호출 체인을 만듭니다. 처음에는 합리적으로 들릴 수 있습니다. 그러나 이 경로를 따라갈 때 고려해야 할 중요한 사항이 있습니다.
차단 및 낮은 성능. HTTP의 동기 특성으로 인해 원래 요청은 모든 내부 HTTP 호출이 완료될 때까지 응답을 받지 않습니다. 이러한 호출 수가 크게 증가하고 동시에 마이크로 서비스에 대한 중간 HTTP 호출 중 하나가 차단되는 경우를 상상해 보십시오. 그 결과 성능이 영향을 받고 추가 HTTP 요청이 증가함에 따라 전반적인 확장성이 기하급수적으로 영향을 받습니다.
마이크로 서비스를 HTTP와 결합합니다. 비즈니스 마이크로 서비스는 다른 비즈니스 마이크로 서비스와 결합해서는 안 됩니다. 이상적으로, 그들은 다른 마이크로 서비스의 존재에 대해 "알고"해서는 안됩니다. 애플리케이션이 예제와 같이 마이크로 서비스를 결합하는 데 의존하는 경우 마이크로 서비스당 자율성을 달성하는 것은 거의 불가능합니다.
하나의 마이크로 서비스에서 오류가 발생했습니다. HTTP 호출로 연결된 마이크로 서비스 체인을 구현한 경우 마이크로 서비스가 실패하고 결국 실패하면 전체 마이크로 서비스 체인이 실패합니다. 마이크로 서비스 기반 시스템은 부분 실패 시 가능한 한 계속 작동하도록 설계되어야 합니다. 지수 백오프 또는 회로 차단기 메커니즘을 사용하여 재시도를 사용하는 클라이언트 논리를 구현하더라도 HTTP 호출 체인이 복잡할수록 HTTP를 기반으로 오류 전략을 구현하는 것이 더 복잡해집니다.
실제로 내부 마이크로 서비스가 설명된 대로 HTTP 요청 체인을 만들어 통신하는 경우 모놀리식 애플리케이션이 있지만 프로세스 내 통신 메커니즘 대신 프로세스 간에 HTTP를 기반으로 한다고 주장할 수 있습니다.
따라서 마이크로 서비스 자율성을 적용하고 복원력을 향상하려면 마이크로 서비스에서 요청/응답 통신 체인의 사용을 최소화해야 합니다. 비동기 메시지 및 이벤트 기반 통신을 사용하거나 원래 HTTP 요청/응답 주기와 독립적으로(비동기) HTTP 폴링을 사용하여 마이크로 서비스 간 통신에 비동기 상호 작용만 사용하는 것이 좋습니다.
비동기 통신의 사용은 비동기 마이크로 서비스 통합 섹션에서 이 가이드의 뒷부분에 나오는 추가 세부 정보와 함께 설명되어 마이크로 서비스의 자율성 및 비동기 메시지 기반 통신을 적용합니다.
추가 리소스
데이터 일관성 입문서
https://learn.microsoft.com/previous-versions/msp-n-p/dn589800(v=pandp.10)마틴 파울러. CQRS(명령 및 쿼리 책임 분리)
https://martinfowler.com/bliki/CQRS.html구체화된 뷰
https://learn.microsoft.com/azure/architecture/patterns/materialized-viewCharles Row. ACID 및 BASE: 데이터베이스 트랜잭션 처리의 이동 pH
https://www.dataversity.net/acid-vs-base-the-shifting-ph-of-database-transaction-processing/보정 트랜잭션
https://learn.microsoft.com/azure/architecture/patterns/compensating-transaction우디 다한. 서비스 지향적 구성
https://udidahan.com/2014/07/30/service-oriented-composition-with-video/
.NET