본문 바로가기
Spring/WebSocket

[Spring WebSocket] 채팅 서비스 프로젝트에 Kafka 적용

by 진꿈청 2024. 11. 10.

https://asfirstalways.tistory.com/359

 

채팅 서비스는 일반적인 `HTTP` 통신을 사용하는 `stateless` 서비스와는 달리,

`WebSocket`을 사용해 클라이언트와 상시 연결되어 있는 `stateful`한 특성을 가진다.

 

만약, 단일 인스턴스라면 해당 부분은 큰 문제가 되지 않을 것이다.

(왜냐하면, 어차피 해당 인스턴스내에서 모든 작업이 일어나기 때문에 서버가 종료되면 마찬가지로 종료되기 때문)

 

하지만, 문제는 `Scale-Out`시에 발생한다.

 

왜 그런지 아래의 예시와 함께 살펴보자.

 

 

채팅 서비스 Scale Out시 문제점

왜 `stateful` 하면 `Scale-Out`시 문제가 발생할까?

 

만약, 카카오톡과 같은 비슷한 코코아톡이라는 채팅 서비스가 현재 단일 인스턴스를 갖는다고 가정해보자.

 

 

이때, 코코아톡이라는 채팅 서비스에 민수철수가 `WebSocket`으로 연결되어 1번 채팅방에서 서로 채팅을 하고 있다.

막 이런저런 얘기를 하고 있는 도중 코코아톡 서비스가 `Scale-Out`되어 코코아톡2가 생겼다.

 

그리고 이때, 새로운 미영이가 코코아톡2 서버의 1번 채팅방에 등장했다.

이런 경우 미영이가 채팅을 보내면 민수철수에게 전달이 될까?

 

당연히 전달되지 않는다.

 

왜냐? 현재 미영이는 "코코아톡2"1번 채팅방에 접속해있고

민수철수"코코아톡1"1번 채팅방에 있기 때문에

 

"코코아톡1"미영이의 정보를 알 수가 없다.

 

즉, 채팅 서비스 간의 채팅 메시지 동기화가 필요하게 된 것이다.

 

이처럼 유저들이 특정 채팅 서비스에 `WebSocket`으로 연결되어 있는 `stateful`한 상태에서는

채팅 서비스 간의 채팅 메시지 동기화 문제로 `scale-out`이 용이하지 않다.

 

그렇다면 어떻게 이 Scale-Out 문제를 해결해야 할까?

 

 

단순하게 접근하면 채팅 메시지를 받은 "코코아톡2""코코아톡1"에게 직접 메시지를 전송하는 것으로 해결할 수 있다.

(듣기만 해도 어렵고 문제가 많아보일 것이다.)

 

이 방법은 A 채팅 서비스B 채팅 서비스에 직접 메시지를 전송하므로, 채팅 서비스 간의 결합도가 높아진다.

그리고, 또 새로운 채팅 서비스가 등장한다면? 채팅이 갑자기 엄청나게 많아진다면?

 

근본적인 해결책이 되지 않는다.

 

그럼 어떻게 해결해야 할까?

 

니즈는 결국 "채팅 서비스 간 직접적인 결합을 피해 부하를 줄이면서 다른 채팅 서비스로 메시지를 전달"해야 한다.

 

 

이때, 도입할 수 있는 것이 메시지 브로커이다.

 

 

메시지 브로커의 도입

우선 메시지 브로커가 무엇인지 간단하게 알아보자.

 

메시지 브로커(message broker)란?

  • 안정적인 비동기 방식으로 메시지를 교환하여 분산된 시스템 혹은 애플리케이션 간의 통신을 돕는다.
  • 생산자와 소비자 간 중개자 역할을 하므로, 두 시스템이 완전히 독립적으로 작동할 수 있도록 돕는다.
  • 컴포넌트 간 느슨한 결합(loose coupling)을 돕는다.
    • 이 구조는 확장성을 증대시켜 주고 장애가 미치는 여파를 줄일 수 있다.
  • 메시지 큐, 토픽과 같은 데이터 구조를 통해 메시지를 임시로 저장해 두었다가 적절한 수신자에게 보내준다.

 

 

이처럼 메시지 브로커를 도입하게 되면 채팅 서비스는 직접 "다른 채팅 서비스로 메시지를 보내는게 아니라"

"메시지 브로커에 메시지를 보내고" 다른 채팅 서비스들은 그냥 메시지 브로커로부터 메시지를 받아오면 된다.

 

이러한 방식을 통해 채팅 서비스 간 메시지 동기화를 해결할 수 있고,

채팅 서비스에서 직접 보내는 것으로 발생하는 부하, 높은 결합도, 여타 다른 문제(새로운 서비스의 등장)등을 
해결할 수 있다.

 

 

아래는 메시지 브로커의 예시이다.

 

 

나는 여기서 메시지 브로커로 `Kafka`를 사용했다.

 

Kafka는 `Message Broker`의 선두주자로 고가용성확장성에 있어 큰 장점을 갖는다.

  • 고가용성
    • `Apache Kafka`는 다수의 브로커로 `Cluster` 구축이 가능하다.
      • 이로 인해, 하나의 브로커가 다운되더라도 다른 브로커가 대신 처리할 수 있다.
      • 즉, `SPOF(Single Point Of Failure)`을 해결할 수 있다.
        • 내부적으로 과반수 쿼럼을 사용함.
  • 확장성
    • `Apache Kafka`는 수평적인 확장이 가능하다.
    • 즉, 브로커를 추가하면 시스템의 처리량도 확장이 된다.

https://hdbstn3055.tistory.com/89

 

Spring With Kafka

이번 캡스톤 디자인 프로젝트를 진행하며 MSA를 사용하게 되었다. 이에 따라 서비스들을 잘게 나누었고 서비스들간의 비동기 이벤트를 처리하기 위해 Kafka를 사용하게 되었다. 본 포스팅에는

hdbstn3055.tistory.com

 

자세한 내용의 위 포스팅을 참고하자.

 

 

정리

정리하자면 `WebSocket`으로 클라이언트와 연결하면 `stateful`하기에 `scale-out`이 쉽지 않다.

따라서, 이러한 문제를 해결하기 위한 메시지 브로커인 `Kafka`를 활용했다.

 

이로인해 메시지 동기화 문제를 해결할 수 있었다.

 

 

 

참고

FitTrip의 팀장 형님

 

트러블 슈팅 - 채팅 서비스 scalue out 문제

서론저희 팀은 기술적인 도전으로 MSA 아키텍처를 도입하기로 했습니다.MSA의 장점으로는 서비스에 대한 scale out이 용이하다입니다.그렇다면 MSA 도입 시 채팅 서비스도 scale out이 용이할까요?채팅

an-jjin.tistory.com