메세지큐와 AMQP
주로 AWS를 사용해서 오픈소스 제품을 사용해본 경험이 적다.
그런데 최근에 이직 공고마다 있는 내용이라 따로 공부해보려고 한다.
(최근에 SQS FIFO관련 글과 연관이 있어서 추가로 정리하는것)
2019/02/10 - [프로그래밍/Cloud] - SQS FIFO seoul 리전 추가
바로 대용량 처리를 위한 kafka
AWS에서는 SQS라는 제품으로 대체할 수 있는데
그래도 어떤점이 좋은지 다른 오픈소스와 어떤점이 다른지 공부해본다.
(카테고리가 애매해서 아키텍쳐 카테고리를 새로 만들었다)
(내용이 길어져서 kafka는 다음 글에서 설명하는걸로)
메세지큐
위키백과에서 메세지큐를 검색하면 다음과 같은 설명이 되어있다.
메시지 큐(message queue)는 키보드나 마우스를 통해 발생하는 사용자의 입력을 메시지로 전달하는 윈도우즈 시스템에서 어떤 프로세스에 대한 메시지를 저장하기 위해 할당된 큐다.
한마디로 하나의 버퍼(임시저장소)라는 것이다.
컴퓨터+키보드를 생각해보면 cpu 연산 중에 키보드에 입력이 들어오면 cpu는 해당 입력을 처리해야된다.
(=IO 인터럽트)
그런데 0.1초 단위로 매번 키보드 입력을 처리한다면 cpu는 0.1초마다 멈춰야하고 그만큼 OS는 느려진다.
그래서 보통 중간에 키보드의 입력을 묶어서 한번에 처리할 수 있도록 하는 버퍼를 두어서 처리를 한다.
1초 동안 키보드 입력을 받아서 한번에 처리하면 원래는 10번 멈춰야할 cpu가 1번만 멈추면 되므로
멈춰야할 시간이 줄지 않겠는가?
(인터럽트 발생으로 인한 context switching 시간 감소)
그런데 메세지큐는 결국 위에서 정의된 기본적인 개념만 있을뿐 따로 표준이 없다.
따라서 구현하는 사람마다 사용법이나 구현방식이 모두 달라서 문제가 많았다.
즉, 표준이 없고 별도의 클라이언트 라이브러리를 사용하는 사람마다 만들거나 메세지큐를 개발한 사람이 직접 개발해줘야 편하게 사용할 수 있다.
결국, 표준이 없어서 쓰기 어렵다는 말이다.
AMQP
AMQP(Advanced Message Queuing Protocol)도 메세지큐다. 이것도 위키의 정의를 찾아보면 다음과 같다.
메시지 지향 미들웨어를 위한 개방형 표준 응용 계층 프로토콜이다. AMQP의 정의 기능들은 메시지 지향, 큐잉, 라우팅(P2P 및 발행-구독), 신뢰성, 보안이다.
쉽게 설명하자면, 표준이 정의된 메세지큐라는 것이다. 대표적으로 kafka, SQS, RabbitMQ 등이 있다. 보통 MSA(Micro Service Architecture)에서 많이 사용되며 오픈소스인 RabbitMQ와 kafka가 많이 사용된다.
AMQP는 와이어 레벨 프로토콜(=미들웨어)이다. 다른 프로토콜과 통신을 하기 위해서 API로 통신하는데 보통 HTTP 방식으로 통신한다. 대부분 HTTP 방식으로 구현되어 있긴 하지만 Restful 하게 URI를 다양하게 구성하는게 아니고 body(payload)로 다양한 data를 주고 받는다. 이게 매우 중요한데, AMQP는 구현체간 상호 운용성을 제공하지 않고 각기 다른 미들웨어 구현체와의 통신 표준화하는데에 초점을 둔 프로토콜이다. 즉, 구현 언어와 상관이 없고 어떤 라이브러리를 쓰든 상호 운용이 가능하다는 것이다.
예시
web server에서 D라는 큐를 kafka에 보내고 kafka에는 이미 A, B, C가 쌓여있다.
consumer/worker는 가장 먼저 kafka에 등록된 큐인 A를 가져와서 작업한다.
예를 들어서 보통 AMQP를 사용한다고 하면 위와 같은 구조가 대부분일 것이다. 웹서버에서 kafka에 큐를 넣어주고 consumer/worker라는 녀석들이 kafka에 쌓인 큐에 해당하는 작업을 진행할 것이다. 여기서 web server가 spring/flask/express.js/django 인 것은 필요없다. 단지 kafka에 전달할때는 kafka에서 제공하는 URI에 body에 data만 잘 넣어서 HTTP 요청을 보내면 된다. consumer/worker도 마찬가지로 언어/프레임워크에 상관없이 kafka에서 큐를 가지고 올때는 HTTP 요청을 통해서 큐를 가져온다. 이렇게 AMQP는 하나의 미들웨어로써 다른 프로토콜(서비스)와 통신을 하기 위한 하나의 계층이다.
발행-구독 모델
발생-구독 모델(Publish–subscribe pattern)은 비동기 메세지 패러다임이다. 즉, 하나의 디자인 패턴이다. 일반적으로 메세지 지향 미들웨어 솔루션의 일부이고 대부분의 메세징 시스템에서 AMQP와 발행-구독 모델을 모두 지원한다.
크게 발신자(발행자), 수신자(구독자), 브로커로 구별된다. 역할은 아래와 같이 이뤄져있다.
역할
- 발신자/발행자 : 메세지의 클래스를 정의하고 메세지를 생성해서 수신자에게 전달한다.
- 브로커 : 발신자로부터 수신자에게 메세지를 전달할 루트를 정해줌.
- 수신자/구독자 : 발신자로부터 메세지를 받는다. 수신자는 어떤 발신자에게 메세지를 받을지에 대한 구독을 브로커에 등록한다.
여기서 알아둬야할 건, 수신자는 전체 발행된 메세지의 부분집합을 받는다는 것이다. 그리고 메세지를 분류할 책임이란, 수신자 별로 메세지를 분류하는 프로세스와 처리를 해야한다는 것이다. 이걸 필터링이라고 한다. 필터링은 토픽 기반과 콘텐츠 기반의 2가지 형태가 있다.
필터링
- 토픽 기반 시스템 : 메세지가 토픽으로 발행되거나 논리 채널들로 명명된다. 수신자는 구독하고자 하는 모든 발행 메세지를 받는다. 해당 토픽의 수신자들은 모두 같은 메세지를 받는다.
- 콘텐츠 기반 시스템 : 구독자에 의해 정의된 메세지 콘텐츠나 속성의 제한적 일치시에 전달되고 구독자 스스로 메세지를 분류해야한다.
연결방식(토폴로지)
발신자-브로커-수신자 구조
발신자-수신자 구조
보통 발신자-브로커-수신자 구조를 따른다. 하지만 분산 라우팅과 필터링 기능에 의해 발신자로부터 수신자에게 직접적으로 전달될 수 있다. 이는 필터링이 브로커에서 이뤄질지(토픽 기반) 수신자에서 이뤄질지(콘텐츠 기반)에 따라서 다르게 구성될 수 있다. 콘텐츠 기반의 필터링은 메세지가 애플리케이션 레이어로 전달되기 전에 수신자 내에서 수행될 수 있다.
장점
장점은 느슨한 결합(Decoupling-디커플링)과 확장성이다. 느슨한 결합의 뜻은 발신자와 구독자는 서로 존재를 알 필요도 없고(=토폴로지를 몰라도 된다) 서로를 고려하지 않은 채 동작을 지속할 수 있다. 이런 느슨한 구조로 인해 단순 클라이언트-서버 구족가 아닌 더 많은 확장성을 제공할 수 있다. 발행-구독 모델로 병렬 처리, 메세지 캐싱, 트리기반/네트워크기반 라우팅 등 다양한 방식을 제공할 수 있다는 것이다.
단점
물론 발행-구독 모델이 만능은 아니다. 오히려 장점으로 인한 부작용이 있는데 대표적으로 디커플링으로 인해 애플리케이션에서 end-to-end에서 강한 속성 명세가 어렵다는 것이다. 예를 들면, 서버-클라이언트 개발자가 API를 공유하기 위해서 따로 wiki나 swagger로 API문서를 공유할 것이다. 하지만 작성된 문서 대로 작성하지 않는다면? 당연히 에러가 발생할 것이다. 이런 것을 강한 속성 명세라고 한다. 이렇게 발행-구독 모델에서도 서로 고려하지 않고 동작하다보니 발신자-수신자 간의 명세를 하기도 어렵고 작성된 문서대로 100% 일치하게 개발하기 어렵다. 그렇기 때문에 문제가 발생할 수 있다는 것이다.
그리고 브로커를 이용하는 구조에서 메세지를 보내기 위한 브로커의 동의는 in-band(동일 채널)이기 때문에 보안 문제가 야기될 수 있다. 예를 들어서, 실제로 그러지 않겠지만, 잘못된 구독으로 인해 네이버 어플에서 라인 어플로 알림을 보낼 수 있고 이렇게 잘못된 구독을 추적하기 위해서 브로커가 추적을 위해 리소스를 할당하다가 과부하될 수 있다.
결국, 사람이 실수하면 큰일난다는 소리다.
이렇게 AMQP에 대해서 알아봤고 이제 진짜 kafka를 살펴보자
2019/03/09 - [프로그래밍/아키텍쳐] - kafka 이해(번역)