메시지 스트림을 이용한 비동기 마이크로서비스 아키텍처

시작하기

단일 서비스에서 모든 기능들이 호스팅되는 방식이 하나 둘 씩 만들어내는 문제들을 해결하고자 회사 기술팀은 코드를 몇 개의 서비스로 분리하기로 결정했습니다. 이것을 실천하기 위해 마이크로서비스 아키텍처(microservice architecture)를 학습하면서 동기적인 작업 흐름으로는 달성하기 어려운 요구사항들이 있다는 것을 알게되었습니다. 그래서 메시지 스트림을 이용한 비동기 분산 시스템을 고민하고 학습했습니다. 이 글은 메시지 스트림의 필요성과 기능적 요구사항, 단점 및 보완 방법에 대해 설명합니다.

API 기반 메시징

API는 Microservice Architecture 등 분산된 시스템의 의사소통 수단 중 가장 많이 쓰이는 도구입니다. 그 중에서도 최근에는 Web API가 주로 사용됩니다.

messaging-api

API 기반 메시징은 요청과 응답의 쌍으로 이뤄지기 때문에 동기적 프로세스는 잘 수용합니다. 하지만 서비스 응답 지연을 낮추기 위해 비동기 프로세스를 도입할 경우 API는 최선의 선택은 아닙니다.

동기적 메시징을 사용하면 개별 서비스의 일시적인 장애로 인해 전체 프로세스가 실패할 수 있습니다. Saga 등 장기간 프로세스의 흐름에서 다음 단계로 진행하기 위해 메시지를 전달받아야 할 수신자 서비스가 일시적으로 장애 상태가 되면 재시도 정책(retry policy)이 준비되지 않은 경우 프로세스 흐름이 끊어질 수도 있습니다.

서비스가 API를 기반으로 소통할 경우 서비스들 간의 관계는 복잡해집니다. 기존 프로세스에 새로운 서비스를 추가하는 것은 경우에 따라 작업량이 많아질 수 있습니다. 하나의 이벤트에 대해 새로운 구독자 서비스가 추가될 경우 이벤트 발생자 서비스는 구독자의 수신 API에 대해 알아야 하며 새 서비스 뿐만 아니라 이벤트 발생자 서비스도 배포되어야 합니다. 즉, 서비스간 결합도가 높아집니다.

비동기 메시징은 메시지 큐를 통해 구현될 수 있습니다. 그러나 복잡하게 연결된 서비스 구조에서 모든 개별 메시지 흐름에 대해 메시지 큐를 사용하면 단편적인 문제는 해결되지만 관리 비용이 크게 증가하는 문제가 있습니다.

메시지 스트림을 사용한 서비스 통합

중앙화된 메시지 스트림을 기반으로 한 발행/구독(publish/subscribe) 패턴을 통해 서비스들이 비동기적으로 통합될 수 있습니다.

messaging-stream

메시지 스트림을 사용해 서비스를 통합하면 메시지 흐름이 단순해집니다. 메시지를 발행하는 서비스는 구독 서비스를 지정하지 않고 메시지를 전송할 수 있습니다. 각 서비스는 수신한 메시지 중 자신에게 필요한 명령이나 이벤트를 선별해 처리합니다.

메시지 스트림의 서비스간 결합도를 낮추는 효과로 인해 신규 서비스 추가는 상대적으로 쉬워집니다. 메시징을 위해 서비스가 유일하게 의존하는 대상은 메시지 스트림이기 때문에 새로운 서비스를 배포할 때 메시지 흐름을 통합하는 목적으로 상류(upstream) 서비스와 하류(downstream) 서비스를 수정하거나 배포할 필요가 없습니다.

메시지 스트림은 비동기적으로 동작하기 때문에 여러 서비스를 관통하는 프로세스가 비동기적으로 진행되어 응답 지연을 줄일 수 있습니다. 프로세스의 각 단계는 다음 단계에 대한 명령이나 이벤트를 전달한 뒤 다음 단계가 완료되기를 기다리지 않고 즉시 응답할 수 있습니다.

하지만 메시지 스트림의 장점을 극대화하려면 몇가지 도전과제가 있으며 일부 단점이 보완되어야 합니다.

메시지 전달 신뢰성

메시지 전달 신뢰성

분산 시스템에서 각 서비스는 장애, 배포 지연 등 다양한 이유로 일시적인 불능 상태가 될 수 있습니다. 메시지 스트림은 일시적인 서비스 불능에 대해 일정 수준 범위에서 메시지 전달을 보장해야합니다.

서비스 수평 확장성

서비스 수평 확장성

성능 요구사항을 만족하기 위해 서비스는 수평적으로 확장될 수 있습니다. 메시지 스트림은 서비스들을 대상으로는 발행/구독 패턴을 제공하지만 서비스 내 인스턴스들에 대해서는 경쟁하는 소비자(competing consumer) 패턴, 혹은 메시지 처리 순서가 중요하다면 메시지 분할을 구현해야합니다. 그렇지 않으면 메시지 처리에 대해 수평 확장의 성능 효과를 볼 수 없으며 하나의 메시지가 여러 인스턴스에 의해 중복 처리되는 문제를 가집니다.

하나의 메시지가 중복처리되는 문제는 다른 상황에서도 발생할 수 있기 때문에 메시지의 멱등성(idempotency, f(x)=f(f(x)))을 고려해야합니다. 이것은 메시지 스트림에서 고려할 범위가 아닙니다.

단점 및 보완

중앙화된 메시지 스트림은 몇가지 단점을 가집니다. 운영환경에서 효율적으로 메시지 스트림을 사용하려면 단점을 이해하고 보완책을 강구해야합니다.

높은 간접수준(level of indirection)

하나의 프로세스를 이루는 여러 서비스(혹은 bounded context)들이 메시지 스트림에 의해 약하게 결합되어 간접수준이 높아집니다. 이것은 장점이기도 하지만 단점일 수도 있습니다. 프로그래머는 모듈이 강하게 결합된 경우에 비해 프로세스 전체 흐름을 파악하기 어려워집니다. 이런 비용을 줄이기 위해 문서화가 필요할 수 있습니다. CQRS Messaging Tools는 이것을 도와주는 도구입니다.

동기적 호출 및 조회

비동기적인 프로세스 흐름이 항상 적합한 것은 아닙니다. 도메인 요구사항에 따라 동기적 서비스 호출이 필요할 수 있습니다. 이때 메시지 스트림은 좋은 도구가 아닙니다. 오히려 API가 적합합니다. 조회 서비스 역시 많은 경우 동기적 응답이 요구됩니다. 이런 경우도 메시지 스트림보다는 API가 좋은 도구입니다.

메시지 스트림 장애대응

메시지 스트림 장애

시스템의 모든(혹은 거의 모든) 소통이 메시지 스트림에 의존하기 때문에 모든 서비스가 가용 상태라도 메시지 스트림이 불능 상태가 되면 서비스간 소통이 모두 단절됩니다. 이것은 곧 치명적인 시스템 장애를 의미합니다.

장애대응 보조 메시지 스트림

메시지 스트림 장애대응을 위해 보조 메시지 스트림을 준비하면 메시지 스트림 가동률을 높일 수 있습니다. 하나의 메시지 스트림을 사용할 때 가동률이 x라면 동일한 가동률의 보조 메시지 스트림을 사용할 경우 가동률은 다음 함수로 계산할 수 있습니다.

f(x) = 1 - (1 - x) ^ 2

예를 들어 x0.999(99.9%)라면 f(x)0.999999(99.9999%)입니다.

하지만 메시지 스트림의 가동률을 높이는 것이 주 메시지 스트림 장애로 인해 발생하는 모든 결함을 제거하는 것을 의미하지는 않습니다. 일부 메시지는 발행된 뒤 불능 상태가 된 주 메시지 스트림에 잠겨 메시지들의 순서가 뒤섞일 수 있습니다. 일부 메시지에 결함이 발생하는 것과 전체 시스템이 마비되는 것 중 합리적인 선택을 해야합니다.

공용 클라우드 서비스를 이용한다면 주 메시지 스트림과 보조 메시지 스트림은 지역적으로 분리되는 것이 좋습니다. 동일한 지역에 두 메시지 스트림이 배포되면 함께 불능 상태에 빠질 가능성이 있습니다.

메시지 스트림 부하

소수의 서비스들 사이에 발생하는 대량의 메시징이 전제 시스템 성능에 영향을 줄 수 있습니다. 이런 경우는 중앙화된 메시지 스트림를 이용하기보다는 별도의 단순한 메시지 큐나 웹훅(webhook) 등을 추가하는 것이 효과적입니다.

메시지 스트림 구현체

이 글에서 언급된 요구사항을 만족하는 메시지 스트림을 쉽게 구현하기 위해 사용할 수 있는 도구는 다음과 같은 것들이 있습니다.

관련 자료

Advertisements

답글 남기기

아래 항목을 채우거나 오른쪽 아이콘 중 하나를 클릭하여 로그 인 하세요:

WordPress.com 로고

WordPress.com의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

Twitter 사진

Twitter의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

Facebook 사진

Facebook의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

Google+ photo

Google+의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

%s에 연결하는 중