[혼자왔니] 채팅 서버 구현을 통해 알아본 redis와 kafka의 차이점
카테고리: RUAlone
태그: Info
채팅기능이 필요하다..
예를 들어 같이 배달을 시킬 사람을 모집하는 글을 올렸다면,
언제 모일건지, 어떤 메뉴를 시킬 건지, 결제는 어떻게 할 것인지 등등
모인 사람들끼리 대화를 할 수 있는 공간이 필요했다.
해당 기능 추가를 위해 채팅용 서버를 토이 프로젝트로 진행해보았다.
pub/sub 구조란?
메시지를 공급하는 주체와 소비하는 주체를 분리하여 제공하는 메시징 방법
- 채팅방(우체통)을 생성한다:
pub/sub
구현을 위한Topic
이 하나 생성된다. - 채팅방(우체통)에 입장한다:
Topic
을 구독한다. - 채팅방에서 메시지를 보내고 받는다: 해당
Topic
으로 메시지를 발송하거나(pub
-집배원) 메시지를 받는다(sub
-구독자)
Web Socket과 STOMP
- Web Socket
- Websocket만을 이용해 채팅을 구현하면 해당 메시지가 어떤 요청인지, 어떻게 처리해야 되는지에 따라 채팅룸과 세션을 일일이 구현하고 메시지 발송 부분을 관리하는 추가 코드를 구현해 줘야한다.
- STOMP
- 메시징 전송을 효율적으로 하기 위해 나온 프로토콜
- pub/sub 구조로 되어있어 메시지를 발송하고, 메시지를 받아 처리하는 부분이 명확이 구분되어 있어 개발 시 명확히 인지하고 개발하기 쉽다는 이점이 있음
- 통신 메시지의 헤더에 값을 세팅할 수 있어 헤더 값을 기반으로 통신 시 인증처리를 구현하는 것도 가능함
채팅 서비스의 고도화
web socket과 STOMP 만을 이용해서 만든 채팅 서버를 고도화시켜보자..!
- 서버를 재시작 할때마다 채팅방 정보들이 리셋된다.
- 채팅방의 저장소가 없으므로 서버의 메모리에 적재된 채팅방은 서버를 재시작할 때마다 초기화되는 이슈가 있다.
- DB 혹은 다른 저장소를 이용하여 채팅방이 계속 유지되도록 처리가 필요함(
redis
)
- DB 혹은 다른 저장소를 이용하여 채팅방이 계속 유지되도록 처리가 필요함(
- 채팅방의 저장소가 없으므로 서버의 메모리에 적재된 채팅방은 서버를 재시작할 때마다 초기화되는 이슈가 있다.
- 채팅서버가 여러대일 때 서버간 채팅방 공유가 불가능함
websocket
과STOMP
pub/sub
을 이용하여 구현할 시,pub/sub
이 발생한 서버 내에서만 메시지를 주고받는 것이 가능함- 채팅방(
topic
)이 생성된 서버 안에서만 유효하므로 다른 서버로 접속한 클라이언트는 접근 및 구독이 불가능하다.
- 채팅방(
- 즉, 구독 대상(채팅방:
topic
)이 여러 서버에서 접근할 수 있도록 개선이 필요하다. (redis
,kafka
,rabbitMQ
등등)
두 가지 방법으로 구현해본 채팅 서버
spring boot와 web socket 및 message queue를 이용하여 채팅 서버를 만들어봤다.
구체적인 구현 방법은 잘 정리해놓은 블로그를 reference에 남겨놓는 것으로 하고,
redis
, kafka
이렇게 총 2가지 message queue를 이용해서 구현해보면서 느꼈던 차이점에 대해서 정리해보려고 한다.
전반적인 동작 과정은 다음과 같다.
- 채팅방을 생성하면,
redis
저장소에 채팅방 정보를 저장 - 채팅방에 입장하면
web socket
연결이 수행되고, 해당 채팅방에 대한subscribe
(구독)을 수행한다. - 해당 채팅방에 message request를 보내면, message를 받아서
message queue
(redis
orkafka
)에publish
한다. message queue
(redis
orkafka
)에 대한subscriber
가web socket
구독자에게 해당 채팅 메시지를 보낸다.
Redis를 사용하여 구현
redis
를 사용할 경우,
message가 publish
되면 해당 channel
을 구독하고 있는 모든 subscriber
에게 message를 보내주게된다.
따라서, 각기 다른 채팅 서버를 통해 접속했더라도 같은 채팅방에서 서로 대화할 수 있는 것이다.
다만, redis
는 보낸 message를 따로 저장하지는 않기 때문에, 해당 channel
을 subscribe
하고 있지 않다면, message가 유실될 수 있다는 단점이 있다.
[8080 서버: 짱구 & 철수, 8090 서버: 맹구]
Kafka를 사용하여 구현
kafka
를 사용할 경우,
message가 publish
되면 해당 message는 topic
의 각 partition
에 분산되어 저장된다.
따라서, redis
와는 다르게 message의 유실에서는 더 안전하다고 볼 수 있다.
또, kafka
의 경우 consumer
가 message를 가져오는 방식으로 동작한다.
각 message에 offset
을 할당해서 할당된 offset
을 기반으로 consumer
는 다음으로 가져올 메시지의 위치를 지정하여 메시지를 가져오게 된다.
이러한 특징 때문에 메시지의 순서를 보장할 수 있다.
자, 여기까지만 알고, 기존에 짰던 redis
기반의 채팅 서버를 kafka
로 바꾸니까 원하는대로 동작하지 않았다.
뭐가 문제일까..
각각 8080과 8090포트로 서버를 띄워 접속해봤고, 채팅 메시지가 제대로 전달되지 않는 것을 확인할 수 있었다.
[8080 서버: 짱구 & 철수, 8090 서버: 맹구]
그리고 medium에서 왜 그런 것인지 확인할 수 있었다.
redis와 kafka의 동작 방식 차이
redis
는kafka
와는 달리,channel
에 도착한 message를 해당channel
을 구독하고 있는 ‘모든’subscriber
들에게 쏴 줄 수 있다.
따라서, 각각 다른 서버여도 같은channel
을 구독하고 있기만 하면 message를 받아볼 수 있는 것이다.
medium에서는 이를 TV 채널을 시청하는 것에 비유하였다.
해당 방송사(producer
)에서 방영하는 라이브 방송은 해당 채널을 시청 중일 때, 같은 시간대에 같은 채널의 시청자들은 모두 같은 방송을 볼 수 있는 것에 비유했다.
하지만
kafka
는 다르다.
topic
에 message가 도착하면, 이를 소비하는 것은consumer group
단위로 이루어진다.
즉,consumer
들이 같은group
에 속해 있다면, message는 해당group
에 있는 어느consumer
중 하나에 의해서 소비된다는 것이다.
medium에서는 이를 우체통에 비유하였다.
철수(producer
)가 편지를 써서 우체통(topic
)에 넣어두었다면, 해당 편지는 누군가에 의해 발견될 때까지는 남아있을 것이고,
가족(consumer group
) 중 어느 누군가에 의해 발견될 수 있을 것이다.
만약 영희네 오빠가 먼저 발견했다면, 영희는 편지의 존재조차 모르게 되는 것이다.
그럼 어떻게 해야할까..?
애초에 message queue를 둔 이유도, scale-out 등의 이유로 서버가 여럿이 있을 때 해결하려고 도입했던 것인데, kafka
에서는 어떻게 해결할 수 있을까?
이 말에서 힌트를 얻었다.
위의 그림처럼 서버 별로 group id를 다르게 해주었다.
아쉽게도 동적으로 할당한 것은 아니지만, group id를 서버별로 다르게 하고, 빌드하여 테스트를 진행해보았다.
[8080 서버: 짱구 & 철수, 8090 서버: 맹구]
(코드 보러가기)
https://github.com/hi-june/chat-demo
Reference
1) https://www.daddyprogrammer.org/post/4077/spring-websocket-chatting/ [websocket으로 채팅 서버 만들기] 2) https://medium.com/frientrip/pub-sub-%EC%9E%98-%EC%95%8C%EA%B3%A0-%EC%93%B0%EC%9E%90-de9dc1b9f739 [pub/sub 잘 알고 쓰자!]
댓글 남기기