MSA와 메시지 큐, Kafka

송준섭 Junseop Song·2024년 8월 28일
post-thumbnail

참고

https://mozzi-devlog.tistory.com/34
https://velog.io/@holicme7/Apache-Kafka-카프카란-무엇인가
https://velog.io/@choidongkuen/서버-메세지-큐Message-Queue-을-알아보자#-메세지-지향-미들웨어mom


개요

이번에 우리 서비스에 FCM을 이용한 푸시 알림을 구현하게 되었다.
원래 우리 백엔드는 하나의 API 서버로 운영되었고, 댓글 생성과 같은 특정 API의 마지막에 푸시 알림을 전송하는 코드를 넣어주려고 하였다.
그러나 같은 백엔드 팀원분께서 아예 알림 서버를 분리하고, Kafka를 이용해 처리하자고 제안을 해주셨다.
물론 서비스 규모가 작아 과하게 느껴질 수 있지만, 학습 목적이 컸기 때문에 적용해 보고자 한다.
그래서 우선 MSA와 메시지 큐 시스템에 대해 알아보고자 한다.

MA

모놀리식 아키텍처(MA)는 전통적인 개발 방식으로 하나의 프로젝트에 모든 기능을 함께 포함하는 구조
코드 베이스가 커질 수록 개발 및 배포에 복잡성이 증가

초기 개발에 유리하며 빠르게 프로토타입을 개발할 수 있음
필요한 모든 기능을 한 번만 호출하기 때문에 복잡한 통신 없이 직접 사용할 수 있음

그러나 코드 베이스가 커질 수록 복잡해지고 유지보수 및 확장이 어려움
일부 기능을 수정하거나 업데이트를 하려면 전체 애플리케이션을 재배포 해야함

간단한 소규모 프로젝트나 프로토타입 제작 및 단기 프로젝트에 적합

MSA

마이크로 서비스 아키텍처(MSA)는 작고 독립적인 서비스들의 집합으로 구성된 애플리케이션 구조
전체 시스템이 분산되어 있어 개발, 배포를 독립적으로 진행할 수 있으며 확장성과 유지관리가 용이

서비스 간 독립성으로 인해 확장성과 유연성이 높아짐
기능 고립성이라는 특징 때문에 일부 서비스가 실패하더라도 전체 시스템에 큰 영향을 미치지 않음

그러나 서비스간 통신이 필요하며, 서로 간 연결 구축 및 관리의 복잡성이 증가함
초기 개발 및 통신 등에 시간이 소요됨

대규모 및 복잡한 프로젝트나 독립적인 시스템을 개발하고 확장해야 하는 경우에 적합

메시지 지향 미들웨어 (MOM)

우선 메세지 지향 미들웨어란 응용 소프트웨어 간의 비동기적 데이터 통신을 위한 소프트웨어
즉, 비동기적(Asynchronous) 한 방식을 이용해서 프로세스간의 데이터를 주고 받는 기능을 위한 시스템

메시지 지향 미들웨어는 아래와 같은 장점이 있음

  • 메세지를 전달하는 과정에서 보관하거나 라우팅 및 변환 가능
  • 메세지의 백업 기능을 유지함으로써 지속성을 제공하며 송수신 측은 동시에 네트워크 연결을 유지할 필요가 없음
  • 미들웨어 계층 자신이 직접 메세지 라우팅을 수행하기 때문에, 하나의 메세지를 여러 수신자에게 배포가 가능
  • 송수신 측의 요구에 따라 전달하는 메세지를 변환

메시지 큐는 이러한 메시지 지향 미들웨어에 속함

메시지 큐 (Message Queue)

일단 큐(Queue)란 선입선출의 자료구조
메세지 큐(Message Queue)란 Queue 자료구조를 채택해서 메세지를 전달하는 시스템으로, 메세지 지향 미들웨어(MOM) 을 구현한 시스템
메세지를 발행하고 전달하는 부분을 Producer, 메세지를 받아서 소비하는 부분을 Consumer라고 부름
메세지 큐는 Producer 와 Consumer 의 메세지 전달 역할을 하는 매개체
MSA(Microservice Architecture)에서 어플리케이션 끼리의 상호작용에서 발생하는 데이터의 흐름을 담당하는 핵심적인 역할

메시지 큐에는 브로커라는 개념이 존재
메세지 큐가 메세지 혹은 이벤트가 송신되고 수신되는 하나의 통신 통로라고 하면 브로커는 메세지 큐에 메세지 혹은 이벤트를 넣어주고 중개하는 역할을 하는 주체
메시지 큐에서 데이터를 운반하는 방식에 따라 브로커는 메세지 브로커 와 이벤트 브로커 로 나눌 수 있음

  • 메시지 브로커
    메세지 브로커는 Producer가 생산한 메세지를 메세지 큐에 저장하고, 저장된 메세지를 Consumer가 가져갈 수 있도록 함
    메세지 브로커는 Consumer가 메세지 큐에서 데이터를 가져가게 되면 짧은 시간 내에 메세지 큐에서 삭제되는 특징이 있음
    ex) RabbitMQ, ActiveMQ, AWS SQS, Redis
  • 이벤트 브로커
    이벤트 브로커 또한 기본적으로 메세지 브로커의 역할을 할 수 있음
    하지만 반대로 메세지 브로커는 이벤트 브로커의 기능을 하지 못함
    이벤트 브로커가 관리하는 데이터를 이벤트라고 하며 Consumer가 메세지 큐에서 데이터를 가져가게 되면 짧은 시간 내에 메세지가 삭제되는 것과 달리, 이벤트 브로커 방식에서는 Consumer가 소비한 데이터를 필요한 경우 다시 소비할 수 있음
    또한 메세지 브로커 보다 대용량 데이터를 처리할 수 있는 능력이 있음
    ex) Kafka

메시지 큐의 장점

메시지 큐는 아래와 같은 장점이 있음

  • 비동기(Asynchronous): Queue에 넣어두기 때문에 나중에 처리할 수 있음
  • 낮은 결합도(Decoupling): 애플리케이션과 분리할 수 있음
  • 탄력성(Resilience): 일부가 실패 시 전체에 영향을 받지 않음
  • 과잉(Redundancy): 실패 할 경우 재실행이 가능
  • 신뢰성(Guarantees): 작업이 처리된 걸 확인할 수 있음
  • 확장성(Scalable): 다수의 프로세스들이 큐에 메시지를 보낼 수 있음

1. 비동기

만약 메시지 큐가 없다면 Producer 역할의 애플리케이션은 자신의 메시지를 전달 받는 Consumer 역할의 애플리케이션에게 직접적으로 메시지를 보냄
이 과정이 완료되기 전까지는 다른 메시지 전달 과정은 이루어지지 못하며, 이는 비효율적 → 동기 방식의 단점

만약 메시지 큐를 중간에 배치하면 Producer 애플리케이션은 Consumer 애플리케이션으로 바로 메시지를 보내는 것이 아닌 Queue에 메시지를 넣기만 하면 됨
이 메시지들을 Consumer가 비동기적으로 처리하는 것

2. 낮은 결합도

메시지 큐를 중간에 둠으로써 하나의 서비스를 구성하는 애플리케이션끼리의 결합도를 낮출 수 있음
애플리케이션의 결합도를 낮추면 확장성, 유연성, 유지보수의 용이성 등 여러 장점이 있음

3. 탄력성

탄력성이란 시스템이 예기치 않은 상황 또는 장애에 대응하고 유연하게 대처할 수 있는 능력
특정 서비스 로직에 A → B의 프로세스가 있다고 할 때, 결합도가 높다면 B에서 발생한 장애가 A에도 영향을 미칠 확률이 높아 A 작업도 정상적으로 처리되지 않을 수 있음
그러나 메시지 큐를 활용하면 B 프로세스의 장애가 발생하더라도 A 프로세스의 작업에서 생기는 메시지들을 보관해 두었다가, 장애를 해결한 후 B 프로세스를 진행할 수 있음

4. 과잉

과잉이란 정상적인 메시지 송/수신이 실패하는 경우 재실행이 가능하다는 뜻
메시지 큐가 없다면 위의 예시와 같이 A, B 프로세스는 End-to-End 통신을 하여 B의 장애 기간동안 시스템 응답성 저하, 데이터 불일치, 메시지 유실 등의 여러가지 문제점이 생길 수 있음
메시지 큐를 사용하면 작업을 메시지로 큐에 넣어두고, 장애 기간동안 쌓인 큐를 장애 복구 이후 정상적으로 재시도 및 처리할 수 있음

5. 신뢰성

신뢰성이란 송신된 메시지의 안전하고 확실한 전달을 의미
위에서 설명한 장점들과 비슷하게 장애가 발생하더라도 Consumer는 송신되는 메시지를 안전하고 확실하게 수신할 수 있음

6. 확장성

여기서 확장성은 수평 확장을 의미
기존 메시지 큐를 이용한 통신에서 부하가 증가하거나 클라이언트의 동시다발적인 요청이 증가할 때, 단순히 메시지 큐에 Producer와 Consumer를 추가함으로써 비교적 간단하고 쉽게 확장할 수 있음

메시지 큐의 종류

1. RabbitMQ

메시지 지향 미들웨어(MOM)를 위한 개방형 표준 응용 계층 프로토콜 AMQP 사용
AMQP는 일반적인 메시지 큐와 비슷하지만 Exchange라는 라우터가 존재하며 Binding이라는 개념이 존재

아래는 RqbbitMQ의 구성요소

  • Producer
    요청을 보내는 주체
    메시지를 Exchange에 Publish
  • Consumer
    Producer로부터 메시지를 받아 처리하는 주체
  • Exchange
    Producer로부터 전달받은 메시지를 어떤 메시지 큐로 전송할 지 결정하는 장소
    이러한 기능을 라우팅 기능이라고 할 수 있음
  • Queue
    Consumer가 소비하기 전까지 메시지를 보관하는 장소
  • Binding
    Exchange와 Queue와의 관계
    특정 Exchange가 특정 Queue에 메시지를 보내도록 정의

RabbitMQ는 AMQP 프로토콜을 구현해 놓은 오픈 소스 메시지 브로커

아래와 같은 특징이 있음

  • 신뢰성, 안전성, 성능을 충족할 수 있도록 다양한 기능 제공
  • Broker 중심적인 형태
  • 유연하고 복잡한 라우팅 가능
  • 관리 UI 존재
  • 거의 모든 언어와 운영체제 지원
  • 20kb/sec 정도의 속도
  • 데이터 처리 보단 관리적 측면이나 다양한 기능 구현을 위한 서비스를 구축할 때 사용

RabbitMQ의 처리 과정은 아래와 같음

  1. Producer가 Broker로 메시지를 보냄
  2. Broker 내 Exchange에서 해당하는 Key에 맞게 Queue에 분배 (Binding)
  3. 해당 Queue를 구독하는 Consumer가 메시지를 소비

RabbitMQ는 메시지 큐 내용을 메모리에 저장하여 재가동시 큐 내용이 손실될 수 있으며, Producer와 Consumer의 결합도가 높다는 단점이 있음

2. ActiveMQ

메시지 큐 및 Publish/Subscribe 패턴과 같은 메시지 기반 통신을 추상화하고 표준화한 JMS라는 API가 존재
Java MOM 표준 API로, 소프트웨어 응용 프로그램 구성 요소가 소비하는 요청, 보고서 또는 이벤트로 메시지를 작성, 전송, 수신 및 읽을 수 있는 메시징 표준
다른 시스템의 프로그램이나 다른 언어로 작성된 프로그램이 메시지를 통해 서로 조정할 수 있도록 함

ActiveMQ는 JMS 스펙을 좀 더 사용하기 편리하게 구현한 오픈소스 메시지 브로커

아래는 ActiveMQ의 구성요소

  • Message Broker
    목적지에 안전하게 메세지를 건네주는 중개자 역할
  • Destination
    목적지에 배달될 2가지 메시지 모델 Queue 와 Topic
  • Queue
    메세지가 전달되는 통로 (경합 있음)
  • Topic
    Queue와 비슷한 역할
    그러나 여러 Consumer에게 메세지를 건네줄 수 있음 (경합 없음)

ActiveMQ의 처리 모델

  • Queue 모델의 경우 메세지를 받는 Consumer가 다수일 때 연결된 순서 로 메세지가 제공됩니다.
  • Topic 모델의 경우 메세지를 받는 Consumer가 다수일 때 메세지는 모두에게 제공됩니다.

그리고 이제 우리가 사용하기로 한 Kafka에 대해 조사해 보았음

Kafka

카프카(Kafka)는 파이프라인, 스트리밍 분석, 데이터 통합 및 미션 크리티컬 애플리케이션을 위해 설계된 고성능 분산 이벤트 스트리밍 플랫폼
Pub-Sub 모델의 메시지 큐 형태로 동작하며 분산환경에 특화
RabbitMQ, ActiveMQ는 메시지 브로커지만 Kafka는 이벤트 브로커
링크드인(linked-in)에서 개발

아래는 카프카 개발 전 링크드인의 데이터 처리 시스템

위 구조는 아래와 같은 문제점이 있음

  1. 시스템 복잡도 증가
    통합된 전송 영역이 없어 데이터 흐름을 파악하기 어렵고, 시스템 관리가 어려움
    특정 부분에서 장애 발생 시 조치 시간 증가 (=> 연결 되어있는 애플리케이션들을 모두 확인해야 하기 때문에)
    HW 교체 / SW 업그레이드 시 관리포인트가 늘어나고, 작업시간 증가 (=> 연결된 애플리케이션에 side effect 가 없는지 확인해야 함)
  2. 데이터 파이프라인 관리의 어려움
    각 애플리케이션과 데이터 시스템 간의 별도의 파이프라인 존재하고, 파이프라인 마다 데이터 포맷과 처리 방식이 다름
    새로운 파이프라인 확장이 어려워지면서, 확장성 및 유연성이 떨어짐
    또한 데이터 불일치 가능성이 있어 신뢰도 감소

이러한 문제를 해결하기 위해 모든 시스템으로 데이터를 전송할 수 있고, 실시간 처리도 가능하며, 급속도로 성장하는 서비스를 위해 확장이 용이한 시스템을 만들고자 하였음 → 모든 이벤트/데이터의 흐름을 중앙에서 관리하는 카프카를 개발

아래는 카프카를 적용한 후 데이터 처리 시스템

  • 모든 이벤트/데이터의 흐름을 중앙에서 관리할 수 있게 됨
  • 새로운 서비스/시스템이 추가되도 카프카가 제공하는 표준 포맷으로 연결하면 되므로 확장성과 신뢰성이 증가
  • 개발자는 각 서비스간의 연결이 아닌, 서비스들의 비즈니스 로직에 집중 가능

Kafka의 특징

대용량 실시간 로그처리에 특화되어 설계된 메시징 시스템으로 TPS가 매우 우수
메시지를 메모리에 저장하는 기존 메시징 시스템과는 달리 디스크에 저장 → 카프카를 재시작해도 메시지 유실 우려가 감소
일반적으로 하드디스크는 메모리보다 수백배 느리지만 하드디스크의 순차적 읽기에 대한 성능은 메모리보다 크게 떨어지지 않음 → 카프카는 파일 시스템을 활용한 고성능 디자인으로 성능을 보장
기본 메시징 시스템(rabbitMQ, ActiveMQ)에서는 브로커가 컨슈머에게 메시지를 push해 주는 방식인데, 카프카는 컨슈머가 브로커로부터 메시지를 직접 가져가는 PULL 방식으로 동작 → 컨슈머는 자신의 처리 능력만큼의 메시지만 가져와 최적의 성능을 낼 수 있음

Kafka의 동작 방식

위는 카프카의 내부 구조
카프카는 Pub-Sub 모델의 메세지 큐 형태로 동작

구성 요소는 아래와 같음

  • Topic
    이벤트가 모이는 곳
    각각의 메시지를 목적에 맞게 구분
    메시지를 전송 및 소비할 때 Topic은 입력해야 함
    producer는 topic에 이벤트를 게시하고, consumer는 topic을 구독해 이로부터 이벤트를 가져와 처리
    1개 이상의 Partition으로 구성
  • Partition
    Topic은 여러 Broker에 분산되어 저장되며, 이렇게 분산된 topic을 partition이라고 함 → 분산 처리
    Topic 생성 시 Partition의 수 지정 가능 → 이후 늘릴 수 있음 (줄이는 것은 불가능)
    파티션 내부에서 각 메시지는 offset(고유 번호)로 구분
    만약 Partition이 1개라면 모든 메시지의 순서 보장
    파티션이 여러개라면 Kafka 클러스터가 라운드 로빈 방식으로 분배해서 분산처리되기 때문에 순서 보장 X
    파티션이 많을 수록 처리량이 좋지만 장애 복구 시간이 늘어남
  • Offset
    Consumer에서 메시지를 어디까지 읽었는지 저장하는 값
    컨슈머 그룹의 컨슈머들은 각각의 파티션에 자신이 가져간 메시지의 위치 정보(offset) 을 기록
    이로 인해 컨슈머 장애 발생 후 다시 살아나도, 전에 마지막으로 읽었던 위치에서부터 다시 읽어들일 수 있음
  • Producer
    메시지를 만들어서 카프카 클러스터에 전송
    메시지 전송 시 Batch 처리가 가능
    key값을 지정하여 특정 파티션으로만 전송이 가능
    전송 acks값을 설정하여 효율성을 높일 수 있음
    • ACKS=0 -> 매우 빠르게 전송, 파티션 리더가 받았는지 알 수 없음
    • ACKS=1 -> 파티션 리더가 받았는지 확인 (기본값)
    • ACKS=ALL -> 파티션 리더 뿐만 아니라 팔로워까지 메시지를 받았는지 확인
  • Consumer
    컨슈머 그룹에 속하여 Kafka 클러스터에서 메시지를 읽고 처리
    컨슈머 그룹은 하나의 Topic에 대한 책임, offset 정보를 그룹 간에 공유
    한 개 파티션은 같은 컨슈머 그룹의 여러 개의 컨슈머에서 연결할 수 없음
    메세지를 Batch 처리할 수 있음
    한 개의 컨슈머는 여러 개의 토픽을 처리할 수 있음
    메시지를 소비하여도 메시지를 삭제하지 않고 한 번 저장된 메시지를 여러번 소비도 가능 (메시지는 Kafka delete policy에 의해 삭제)
  • Event
    Kafka에서 producer와 consumer가 데이터를 주고받는 단위. 메세지
  • Broker
    실행된 카프카 서버
    프로듀서와 컨슈머는 별도의 애플리케이션으로 구성되는 반면, 브로커는 카프카 자체
    Broker(각 서버)는 Kafka Cluster 내부에 존재
    서버 내부에 메시지를 저장하고 관리하는 역할을 수행
  • ZooKeeper
    분산 애플리케이션 관리를 위한 코디네이션 시스템
    분산 메시지 큐의 메타 정보를 중앙에서 관리하는 역할

동작 원리는 아래와 같음

  1. publisher(Producer)는 전달하고자 하는 메세지를 topic을 통해 카테고리화
  2. subscriber(Consumer)는 원하는 topic을 구독함으로써 메시지를 읽어옴
  3. publisher와 subscriber는 오로지 topic 정보만 알 뿐, 서로에 대해 알지 못함
  4. Kafka는 broker들이 하나의 클러스터로 구성되어 동작하도록 설계
  5. 클러스터 내, broker에 대한 분산처리는 ZooKeeper가 담당

Kafka는 대규모 트래픽 처리 및 분산 처리에 효과적이며, 클러스터 구성, Fail-over, Replication 같은 기능이 있음
다른 메시지 큐 시스템들보다 빠른 100Kb/sec 정도의 속도를 가지고, 디스크에 메세지를 특정 보관 주기동안 저장하여 데이터의 영속성이 보장되고 유실 위험이 적음
또한 Consumer 장애 시 메시지를 보관해 두었다가 Consumer 복구 후 재처리가 가능

다음에는 Kafka를 Spring Boot에서 사용해 보도록 할 것!

0개의 댓글