개발놀이터
메세지 브로커의 근심과 걱정 본문
마이크로 서비스와 메세지 브로커는 뗄 수 없는 사이입니다. 서로 다른 도메인이 여러개의 서버로 나눠져 있는 상황에서 모든 서버에 동일하게 데이터를 전달해야 하는 경우에 메세지 브로커만한게 없죠.
그렇기에 모놀리식에서는 메세지 브로커를 사용하는게 오버엔지니어링이 되기도 합니다.
이번 포스팅에서는 메세지 브로커의 근심과 걱정에 대한 내용을 공부해보고 정리해봤습니다.
메세지 브로커
https://coding-review.tistory.com/504
메세지 브로커는 위의 포스팅에 간략하게나마 정리가 되어있습니다. 저는 카프카에 대해서 공부했지만 대부분의 메세지 큐잉 서비스들이 비슷한 느낌이 있어서 대략적인 내용을 공부하기엔 좋았습니다.
이번 포스팅에선 메세지 브로커에 대해서 간략하게만 짚고 넘어가도록 하겠습니다.
Pub/Sub 구조와 Topic
메세지 브로커에서 빼놓고 등장하는 개념이 Pub/Sub 메세징인데요. 메세지를 보내는 입장인 Publisher와 메세지를 받는 입장인 Subscriber가 메세지를 주고받는 아키텍처를 말합니다.
이 때 메세지를 보내는 사람과 메세지를 받는 사람이 알고 있어야 하는 것이 바로 Topic입니다.
이는 간단한 예시로 설명이 가능한데 바로 뉴스기자와 구독자입니다.
다른 얘기지만 저는 요즘 AI와 관련된 뉴스기사를 재밌게 보고 있는데 3~4일에 한번씩 기사를 읽으면서 트렌드를 따라가고 있는 편입니다.
하지만 뉴스 기사는 여러가지 "토픽"이 있죠. 스포츠, 게임, 경제, 연예, 기술 등등이요. 뉴스 기자분들은 열심히 본인의 분야에서 기사를 쓸겁니다. 여기서 기자분들이 바로 Publisher이죠.
독자 (Subscriber) 는 말그대로 구독자이고 이 독자들은 자신이 원하는 "주제" (Topic) 의 뉴스기사만을 보고싶어합니다.
저만하더라도 AI만 보고싶으니 다른 주제들은 보고싶지 않거든요. 그럼 저는 AI라는 주제를 구독합니다. 그럼 AI에 관련된 뉴스기사가 뜰때마다 저에게 알람이 올 수 있겠죠? 그럼 독자들 입장에서 굳이 계속 휴대폰을 열어보지 않아도 되니까요.
자 여기까지 Pub/Sub 구조와 Topic에 대해서 모두 설명이 끝났습니다.
메세지 브로커의 Publisher, Subscriber, Topic와 모두 치환 가능합니다.
Publisher는 기자, Subscriber는 독자, Topic은 주제, 그럼 메세지 브로커는? 네이버뉴스나 AI타임즈같은 플랫폼이 되겠네요.
메세지를 어떻게 주고받는지는 위에 링크에 자세히 설명되어있으니 참고해주세요!
메세지 브로커의 근심
메세지 브로커가 가진 근심이 뭘까요? 보통 메세지 브로커를 사용한다면 이런 경우일 가능성이 높습니다.
- 메세지를 보낸다
- 메세지를 받는다
- 데이터베이스에 어떤 작업 (INSERT, UPDATE, DELETE) 을 진행한다
- 이후 다시 메세지를 보낸다.
- 메세지를 받는다
- ....
그런데 이때!
데이터베이스 작업하던 도중 예측 가능한 혹은 예측 불가능한 예외가 터져서 롤백이 됐습니다. 그럼 메세지는 어떡하죠?
물론 당연히 보내면 안됩니다.
어떻게요?
OutBox Pattern
메세지를 보내기 전에 성공했다는 것을 확인할 수 있는 정보를 어딘가에 저장하고 있다가 성공했으면 그 때 메세지를 보내면 됩니다.
의외로 간단하네요.
OutBox 패턴을 구현할 수 있는 방법이 두 가지 있습니다. 바로 Polling Publisher Pattern과 Transaction Log Tailing Pattern입니다.
Polling Publisher Pattern
이 방법은 구현하기는 간단하지만 몇가지 단점이 있는 방법입니다. "어딘가에 저장"이라는 단어에서 직관적으로 느낄 수 있는 것인 데이터베이스 테이블에 저장하는 방법입니다.
데이터베이스에 값을 쓰면서 동시에 이 임의의 테이블 (이하 OutBox 테이블) 에 값을 쓰는겁니다. 이 값이 큐에 들어있는 메세지가 될 수도 있고, 아니면 실제로 저장되는 데이터일 수도 있죠.
그리고 메세지를 보내기 전에 이 OutBox 테이블에 있는 값을 읽어서 있으면 (성공하면) 메세지를 보내면 됩니다.
이 방법은 굉장히 구현하기 간단한 반면 큰 단점이 존재합니다. 바로 메세지를 보내기 전에 두번의 쿼리가 발생한다는 것입니다.
두번의 쿼리가 뭐 어때서 그냥 시원하게 보내면 되지.. 싶다가도 메세지가 한두개가 오는게 아닌걸 생각해보면 리소스가 꽤나 많이 들어가는 작업이라는 것을 알 수 있습니다.
이 단점을 해결한 것이 Transaction Tailing Pattern입니다.
Transaction Tailing Pattern
이 패턴의 이름에서 유추할 수 있듯이 트랜잭션을 따라가면서 어떤 값이 바뀌었는지만 확인하는 방법입니다.
데이터베이스에는 테이블을 업데이트하기 전 (INSERT, UPDATE, DELETE 전) 트랜잭션 로그라는 것을 기록합니다. 우리에겐 WAL로 잘 알려진 이것은 다른 말로 트랜잭션 로그라고도 불립니다.
이 로그를 추적하면서 변경된 값만 보면서 변경된 값이 있으면 그것을 보고 메세지를 보내는 방식입니다.
이를 그림으로 표현하면 다음과 같겠네요.
그런데 이 단점이 없어보일 것 같은 방법에도 단점이 있는데 바로 구현하기가 굉장히 힘들다는 것입니다. 트랜잭션 로그를 추적해야한다는 것을 듣기만해도 난이도가 헬일 것 같은데 추가적인 작업이 남아있습니다.
메세지가 정상적으로 전송되면 메세지를 지워줘야하고 트랜잭션 로그를 추적해서 겨우 메세지를 보내놨더니 굉장히 빈번하게 발생하는 네트워크 오류때문에 메세지가 전송되지 않으면 재전송하는 로직도 필요합니다.
이처럼 메세지 브로커와 트랜잭션을 묶는다는 것은 굉장히 복잡도가 올라갑니다.
이를 구원해줄 해결사가 등장합니다. 바로 PostgreSQL입니다.
젠장, 또 PostgreSQL이야
뜬금없이 다른 RDB도 많은데 PostgreSQL이냐면 위의 많은 패턴에서 겪는 높은 복잡도를 해결해주는 키를 가지고 있기 때문입니다.
바로 PGMQ (Postgres Message Queue) 입니다.
https://github.com/tembo-io/pgmq
PGMQ의 깃허브 문서를 보면 이게 진짜 되나싶을정도로 놀랍긴한데... (실습을 해볼 예정입니다) 이 OutBox 테이블의 역할을 PGMQ가 맡아서 함으로써 메세지를 관리하기 쉽게 해줍니다.
또한, 쿼리로 이루어져있어서 딱히 관리할 것도 없구요. 거기다가 딱히 Citus처럼 난이도 높은 Plugin이 필요한 것도 아닙니다.
위의 아키텍처가 살짝 수정돼서 아래의 그림처럼 됩니다.
오늘도 PostgreSQL이 1승을 추가하네요.
그럼 다른 RDB는 어떻게 해야하냐?
뭐.. 트랜잭션 로그 추적하시던가 OutBox 테이블 만드시던가..
정 안되겠으면 PostgreSQL로 마이그레이션 하는 방법도 있겠네요.
마치며
다음 포스팅에서는 이 내용을 직접 실습해보려고합니다. 사실 마이그레이션은 농담이고 MySQL인 경우에는 어떻게 트랜잭션 로그를 추적해야하는지도 실습해볼겁니다.
메세지 브로커와 데이터베이스가 협업해야 하는 과정에서 생기는 마찰들 때문에 꽤나 시끄러워졌습니다. 이정도로 대공사일 줄은 몰랐는데말이죠.
트랜잭션 로그를 추적한다는 것 자체만으로도 굉장히 겁먹은 상태인데... 시도는 한번 해보도록 하겠습니다.
긴 글 읽어주셔서 감사합니다. 오늘도 즐거운 하루 되세요!
2024 11/26 업데이트
해당 내용에 대한 실습 포스팅이 업로드되었습니다!
https://coding-review.tistory.com/571
'CS 지식 > 데이터베이스' 카테고리의 다른 글
MySQL 트랜잭션 로그 추적 with 스프링 (0) | 2024.11.26 |
---|---|
MySQL에 사는 유령 (0) | 2024.11.09 |
Orchestrator를 이용한 MySQL 레플리케이션 그리고 장애회복 (0) | 2024.08.16 |
나는 왜 Redis Cluster 대신 Redis Sentinel을 사용하였는가. (0) | 2024.08.12 |
PostgreSQL이 WAL (Write Ahead Log) 를 활용하는 방법 (0) | 2024.07.24 |