Redis

PUB/SUB 채팅서비스 redis ? kafka

MIN우 2023. 9. 1. 16:55
728x90

이번 소프트웨어마에스트로 서비스에서 채팅서비스를 운영하면서 웹소켓의 stomp구조를 사용하게되었다.

 

STOMP란 무엇일까 ?

(Simple/Stream Text Oriented Message Protocol)

 

websocket 위에서 동작하는 문자 기반 메세징 프로토콜로써 클라이언트와 서버가 전송할 메세지의 유형 형식

내용들을 정의하는 매니즘

TCP와 웹소켓과 같은 신뢰할 수 있는 양방향 스트리밍 네트워크 프로토콜

기본적으로 pub/sub 구조이며, 메세지 전송하고 받아 처리하는 부분이 확실히 정해져 있다.

http 와 마찬가지로 frame을 사용해 전송하는 프로토콜 

 

여기서 pub/sub 란 무엇인지 또 알아보겠습니다.

 

 

1. 이벤트메세지를 발행하는 publisher가 존재하고, publisher는 특정 토픽(채널)에 이벤트를 전송합니다.

2. 특정 토픽(채널)을 구독하는 subscriber가 존재하고, publisher에 관계없이 발행된 이벤트를 얻을 수 있다.

 

pub/sub는 메시지를 공급하는 객체(publisher)와 소비하는 객체(subscriber)를 분리해 제공하는 비동기식 메시징 방법이다. publisher가 특정 topic에 메시지를 보내면 해당 topic을 구독해놓은 모든 subscriber에게 메시지가 전송되는 방식이다.

 

정말 쉽게 설명하면 유튜브를 예를 들 수 있을 것 같습니다.

 

음.. 어느한 유튜버를 구독하면 , 구독을 한 여러 유저에게 알림을 주는 시스템 이것이 pub/sub형태를

간단하게 요약하면 이리 설명할 수 있을 것 같네요.

 

pub/sub는 위 그림에서 보듯이 서로 직접 통신하는 것이 아닌 Message Broker를 통해 메시지를 전달하는 구조이다. Message Broker는 Publisher가 보낸 메시지를 Subscriber로 전달해주는 중간 다리(미들웨어) 역할을 한다.

왜 중간에 Message Broker가 필요한지에 대한 이유는 다음과 같다.

 

  • 느슨한 결합 (Publisher는 메시지를 발신할 때 다른 서비스들에 대해 알 필요가 전혀 없음, scale-out 용이)

  • 메시지 버퍼링 (Subscriber 측에서 원하는 시점에 메시지를 처리할 수 있음)

 

여기서 고민 , Message Broker도 여러가지가 있다 . 무엇을 사용해야 할까 ?

 

1. In-Memory 기반 Message Broker

Sring 에서 제공하는 STOMP를 활용하고, 내장된 SImple Message Broker 를 사용하여 채팅서버를 구현

할 수 있었다. 하지만 Simple Message Broker 같은 경우 스프링 부트 서버의 내부 메모리에서 동작하게 된다.

 

인메모리 기반 브로커를 사용했을 때 문제점.

 

  • 서버가 down되거나 재시작을 하게되면 Message Broker(메시지 큐)에 있는 데이터들은 유실될 수 있다.

  • 다수의 서버일 경우 서버간 채팅방을 공유할 수 없게 되면서 다른 서버간에 있는 사용자와의 채팅이 불가능 해진다.

우리 서버는 오토스케일링과 로드밸런싱이 이미설정이 되어 있기 때문에 채팅서비스를 도입시

데이터 및 다른 서버간 통신이 불가능해질 수 있다.

 

그래서, In-Memory기반 Message Broker는 패스

 

2. Redis Pub/Sub 

 

Redis는 STOMP 프로토콜을 지원하지 않지만, Redis가 제공하는 Pub/Sub 기능을 통해 메시지 브로커로 사용할 수 있다.

몰론 STOMP 프로토콜을 지원하는 RabbitMQ와 같은 전용 메시지 브로커를 사용하면 더 고도화된 기능(메시지 전달 보장, SSL 지원)을 사용할 수 있다.

하지만 채팅 정보에 대한 세션을 관리할 key-value 데이터베이스가 필요했고, 더 빠르고 쉽게 구현할 수 있는 Redis를 선택하게 되었다.

다중 서버에서 Redis Pub/Sub를 이용하면 아래 그림과 같을 것이다.

 

 

그래서, redis가 다운됐을 경우를 생각해서 Redis 또한 클러스터를 구축하여 , 서버 다운 됐을 때도 데이터가

유실되지 않도록 대비를 해놓았다.

 

그렇다면 요즘 핫한 kafka하고는 뭐가다른가 ?

 

3. kafka pub/sub

Kafka의 PUB/SUB 모델

Kafka에서는 producer/consumer 라는 개념이 등장한다.

각각 pub/sub라고 보면 됩니다. producer 는 topic에 이벤트를 보내고, 이 이벤트는 topic의 각

partition에 분산되어 저장됩니다. topic을 구독하고 있는 consummer group내의 consumer는 각각 1개 이상의

partition으로 부터 이벤트를 가져옵니다. 만약 partition 개수보다 consummer갯수가 많다면 ? 아무일도 하지않는

consummer가 생기지 않기 때문에 항상 Partition 수를 consummer보다 같거나 크게 해주는 것이 좋다.

 

 

KAFKA vs REDIS

내가 생각하는 Kafka와 Redis의 주된 차이점은 다음과 같다.

  1. 이벤트의 저장 여부
  2. 한 이벤트를 받을 수 있는 Subscriber(Consumer) 개수

이벤트의 저장 여부

Kafka는 발행된 이벤트가 각 Partition에 저장된다. 하지만 Redis는 발행된 이벤트를 저장하지 않기 때문에, 구독자가 없다면 해당 이벤트는 사라지고 만다. 따라서, 이벤트의 구독과 발행이 실시간으로 이루어져야 되는 상황인지, 혹은 언제든 발행된 이벤트를 읽으면 되는 상황인지에 따라 선택이 달라질 것이다.

 

TMI 그래서 나는 reids에서 hashOperation으로 값을 저장하고 있다.

  public void saveChatting(String chattingRoom, ChatMessage chatting) {
        HashOperations<String, String, ChatMessage> hashOperations = redisTemplate.opsForHash();
        hashOperations.put(chattingRoom, chatting.getId(), chatting);
        log.info("create new chatting [{}] int chatting room '{}'", chatting, chattingRoom);
    }

한 이벤트를 받을 수 있는 Subscriber(Consumer) 개수

한 API에 대해, Scale-out 등의 이유로, 여러 서버 인스턴스가 작동될 수도 있다. 예시를 생각해보자. Coupon 서비스는 유저 회원 가입에 대한 Topic을 구독하고 있고, 유저가 회원가입 했다는 이벤트를 받으면 회원가입 기념 쿠폰을 발행한다.

그리고 이때, Coupon 서비스에 대해 2개의 서버를 사용한다고 하자.

만약 Kafka를 사용한다면, Consumer의 수와 관계없이, 유저 회원가입당 하나의 쿠폰이 발행될 것이다.

하지만, Redis 를 사용한다면?

유저는 한번의 회원가입으로 두 개의 쿠폰을 얻게되는 상황을 얻게 된다.

 

즉, 한 이벤트에 대해 한 번의 기능만 작동되어야 한다면 Kafka를 사용하는 것이 유리하다.

 

반대로 Redis의 PUB/SUB 기능이 필요할 때도 있다.

예를 들어, 내가 개발한 Gateway의 권한 플러그인의 경우, 존재하는 모든 권한 목록을 서버가 ‘시작될 때’ 가져온다.

이때 한 가지 문제가 있다. 새로운 권한이 추가되거나, 삭제되었을 때에도, 서버를 재시작하기 전까지는 권한 목록을 갱신할 수가 없는 것이다.

 

이처럼 발행된 이벤트에 대해, 특정 작업이 한 번만 발생하여야 하는지, 모든 Subscriber에서 발생되어야 하는지에 따라 다른 선택을 해야 한다.

 

그래서 redis는 토픽에 데이터가 저장되지않는다는 것을 미리알고

서버간 채팅방 공유를 위해 redis hash에 데이터를 저장하고

이 값으로 서로 데이터를 주고 받을 수 있도록 하였습니다.

 

결국 우리가 선택한 것은 처음 사용해보기에 적합하고 여러서버 간 채팅을 동기화 할 수 있는 redis

를 이용하여 클러스터 구축 및 채팅을 Pub/sub형태로 구성할 것 입니다. 추후에

더욱 다양한 기능을 제공하고 대용량 이벤트 스트림 처리에 적합한 kafka를 통해 채팅서버의

데이터를 동기화시키는 작업을 하겠습니다.

 

 

 

 

 

728x90