조정을 최소화하여 확장성 달성
대부분의 클라우드 애플리케이션은 웹 프런트 엔드, 데이터베이스, 비즈니스 프로세스, 보고 및 분석 등 여러 애플리케이션 서비스로 구성됩니다. 확장성과 안정성을 달성하려면 이러한 각 서비스가 여러 인스턴스에서 실행되어야 합니다.
컴퓨터 간에 메시지를 전달할 필요 없이 작업을 독립적으로 처리할 수 있는 조정되지 않은 시스템은 일반적으로 크기 조정이 더 간단합니다. 조정은 일반적으로 이진 상태가 아니라 스펙트럼입니다. 조정은 데이터 또는 컴퓨팅과 같은 여러 계층에서 발생합니다.
두 인스턴스가 일부 공유 상태에 영향을 주는 동시 작업을 수행하려고 하면 어떻게 되나요? ACID 보장 유지 등을 위해 노드 간에 조정이 필요한 경우도 있습니다. 이 다이어그램에서 Node2
는 Node1
에서 데이터베이스 잠금을 해제할 때까지 대기 중입니다.
조정은 수평적 확장의 이점을 제한하고 병목 상태를 만듭니다. 이 예제에서는 애플리케이션을 확장하고 인스턴스를 추가함에 따라 잠금 경합이 증가합니다. 최악의 경우 프런트 엔드 인스턴스는 잠금을 기다리는 데 대부분의 시간을 소비합니다.
"정확히 한 번" 의미 체계는 조정의 또 다른 빈번한 소스입니다. 예를 들어 주문이 정확히 한 번 처리되어야 합니다. 두 명의 근로자가 새로운 주문을 듣고 있습니다. Worker1
이 처리를 위해 주문을 선택합니다. 애플리케이션은 작업이 중복되지 않도록 해야 Worker2
하지만 충돌하는 경우에도 Worker1
주문이 삭제되지 않습니다.
Scheduler 에이전트 감독자와 같은 패턴을 사용하여 작업자 간에 조정할 수 있지만, 이 경우 작업을 분할하는 것이 더 나은 방법일 수 있습니다. 각 작업자에게 특정 범위의 주문(가령, 청구 지역별)이 할당됩니다. 작업자가 충돌하는 경우 새 인스턴스는 이전 인스턴스가 중단된 위치를 선택하지만 여러 인스턴스가 경합하지 않습니다.
권장 사항
비동기적으로 통신하는 분리된 구성 요소를 사용합니다. 구성 요소는 이벤트를 사용하여 서로 통신하는 것이 이상적입니다.
결과적 일관성 보장. 데이터가 분산되면 강력한 일관성 보장을 적용하려면 조정이 필요합니다. 예를 들어 작업에서 두 개의 데이터베이스를 업데이트한다고 가정합니다. 단일 트랜잭션 범위에 배치하는 대신, 시스템이 최종 일관성을 수용할 수 있는 경우, 아마도 보상 트랜잭션 패턴을 사용하여 오류 후 논리적으로 롤백하는 것이 좋습니다.
도메인 이벤트를 사용하여 상태를 동기화합니다. 도메인 이벤트는 도메인 내에서 중요한 문제가 발생할 때를 기록하는 이벤트입니다. 글로벌 트랜잭션을 사용하여 여러 서비스 간에 조정하는 대신 관심 있는 서비스가 이벤트를 수신 대기할 수 있습니다. 이 방법을 사용하는 경우 시스템은 최종 일관성을 허용해야 합니다(이전 항목 참조).
CQRS 및 이벤트 소싱과 같은 패턴을 고려합니다. 이러한 두 패턴은 읽기 워크로드와 쓰기 워크로드 간의 경합을 줄이는 데 도움이 될 수 있습니다.
CQRS 패턴은 읽기 작업을 쓰기 작업과 구분합니다. 일부 구현에서는 읽기 데이터가 쓰기 데이터와 물리적으로 분리됩니다.
이벤트 소싱 패턴에서 상태 변경 내용은 추가 전용 데이터 저장소에 대한 일련의 이벤트로 기록됩니다. 스트림에 이벤트를 추가하는 것은 최소한의 잠금이 필요한 원자성 작업입니다.
이러한 두 패턴은 서로를 보완합니다. CQRS의 쓰기 전용 저장소에서 이벤트 소싱을 사용하는 경우 읽기 전용 저장소는 동일한 이벤트를 수신 대기하여 쿼리에 최적화된 현재 상태의 읽기 가능한 스냅샷을 만들 수 있습니다. 그러나 CQRS 또는 이벤트 소싱을 채택하기 전에 이 방법의 과제에 유의해야 합니다.
파티션 데이터 및 상태입니다. 모든 데이터를 여러 애플리케이션 서비스에서 공유되는 하나의 데이터 스키마에 넣지 않도록 합니다. 마이크로 서비스 아키텍처는 각 서비스가 자체 데이터 저장소를 담당하게 하여 이 원칙을 적용합니다. 단일 데이터베이스 내에서 데이터를 분할된 데이터베이스로 분할하면 하나의 분할된 데이터베이스에 대한 서비스 쓰기가 다른 분할된 데이터베이스에 대한 서비스 쓰기에 영향을 주지 않으므로 동시성이 향상될 수 있습니다. 분할은 어느 정도의 조정을 추가하지만, 더 나은 확장성을 위해 분할을 사용하여 병렬 처리를 늘릴 수 있습니다. 데이터를 독립적으로 관리할 수 있도록 모놀리식 상태를 더 작은 청크로 분할합니다.
idempotent 작업을 디자인합니다. 가능하면 멱등원으로 작업을 디자인합니다. 이렇게 하면 최소 한 번 이상의 의미 체계를 사용하여 처리할 수 있습니다. 예를 들어 작업 항목을 큐에 배치할 수 있습니다. 작업 도중 작업자 작동이 중단될 경우 다른 작업자가 작업 항목을 선택하면 됩니다. 작업자가 데이터를 업데이트하고 다른 메시지를 논리 의 일부로 내보내야 하는 경우 idempotent 메시지 처리 패턴을 사용해야 합니다.
가능하면 낙관적 동시성을 사용합니다. 비관적 동시성 제어는 데이터베이스 잠금을 사용하여 충돌을 방지합니다. 이로 인해 성능이 저하되고 가용성이 저하될 수 있습니다. 낙관적 동시성 제어를 사용하여 각 트랜잭션은 데이터의 복사본 또는 스냅샷을 수정합니다. 트랜잭션이 커밋되면 데이터베이스 엔진은 트랜잭션의 유효성을 검사하고 데이터베이스 일관성에 영향을 주는 트랜잭션을 거부합니다.
Azure SQL Database 및 SQL Server는 스냅샷 격리를 통해 낙관적 동시성을 지원합니다. 일부 Azure Storage 서비스는 Azure Cosmos DB 및 Azure Storage를 포함하여 Etag를 사용하여 낙관적 동시성을 지원합니다.
MapReduce 또는 다른 병렬 분산 알고리즘을 고려합니다. 수행할 작업 유형 및 데이터 및 유형에 따라 병렬로 작업하는 여러 노드에서 수행할 수 있는 독립적인 작업으로 작업을 분할할 수 있습니다. 빅 컴퓨팅 아키텍처 스타일을 참조하세요.
조정을 위해 리더 선택 사용. 작업을 조정해야 하는 경우 코디네이터가 애플리케이션의 단일 실패 지점이 되지 않도록 합니다. 리더 선거 패턴을 사용하여 한 인스턴스는 언제든지 리더이며 코디네이터 역할을 합니다. 리더가 실패하면 새 인스턴스가 리더로 선택됩니다.