사이드 프로젝트를 진행하면서 채팅 기능을 담당하게 되었다.
채팅 기능에 대한 요구사항
프로젝트에서 사용할 기술에 대해 회의를 진행하면서 외부 브로커를 사용하기로 결정하였다. 왜냐하면, 프로젝트의 인프라 환경은 단일 서버이지만, 채팅 기록을 저장해야하는 요구사항이 있기 때문이다.
즉, 단일 서버이지만, 데이터베이스에 채팅 기록을 저장하기 위해 디스크 I/O가 발생하기 때문에 latency를 낮추기 위해 외부 브로커를 사용하기로 결정하였다.
이처럼 채팅 서비스를 구현하기 위해서 어떤 Pub/sub 메시지 브로커를 사용할 것인지 결정해야한다.
대표적으로 Redis와 Kafka가 존재한다.
우선 Redis에 대해 알아보자.
Redis는 NoSQL DB, In-Memory DB, PUB/SUB 등으로 다양하게 사용되는데 서로 다른 서버에서 채팅 메시지를 수신하기 위해서 사용되는 PUB/SUB에 대해 알아보려고한다.
Redis Pub/Sub의 가장 큰 특징으로는, In-Memory 기반이기 때문에 low-latency로 메시지를 구독자들에게 전달할 수 있지만, 메시지가 영속적으로 저장되지 않는다.
그리고 Redis Pub/Sub은 발행자가 메시지를 Publish 하는 순간, 해당 채널을 구독한 구독자들에게 메시지를 바로 전달한다.(Push 모델)
즉, 구독자들은 메시지를 발행한 시점에만 메시지를 전달 받을 수 있다는 것이다.
만약에 채널에 대해 구독자들이 없다면 메시지가 발생되어도 바로 사라진다는 것이다.
이번에는 Kafka에 대해 알아보자.
Kafka는 분산, 수평 확장 가능한 Pub/Sub 메시지 시스템이며, 특징으로는 High-Throughput, High-Availability, fault-tolerant가 있다.
Kafka는 Producer가 Topic에 이벤트를 보내면, 해당 이벤트를 Topic의 각 Partition에 분산하여 저장한다. 그리고 Consumer는 주기적으로 Topic에 메시지가 있는지 확인하고 Polling하여 Consumer가 메시지를 가져온다.
그리고 Consumer가 메시지를 가져가더라도 Topic의 메시지가 바로 삭제되지 않는다.
-> 보관주기/용량이 넘어가면 삭제된다.
그리고 메시지를 Polling하는 Consumer는 Group으로 관리되며, Group내 Consumer는 각각 1개 이상의 Partition으로부터 이벤트를 가져온다.
Redis의 Pub/Sub과 Kafka를 비교해보았을 때, 현재 우리 프로젝트에 알맞은 기술은 Redis를 사용해야할 것 같다는 생각이 들었다.
일반적으로 Kafka를 사용하는 이유는 대표적으로 고가용성과 신뢰성 등이다.
하지만 우리 프로젝트 환경은 단일 서버 환경이라는 점과 외부 데이터베이스(몽고DB 등)를 추가적으로 구축한다는 점을 고려했을 때, 신뢰성이 문제가 되지 않을 것이라고 생각이 들었다.
또한, 실시간 채팅이라는 기능을 고려했을 때, Kafka보다는 매우 빠른 인메모리 기능을 제공하는 Redis를 사용하는 것이 성능이 더 좋을 것 같다는 생각이 들었다.
그러면 단일 서버 환경이면서, 외부 DB를 따로 구축할 것이면, Redis를 사용하지 않고, STOMP의 내부 브로커가 메시지를 전달받았을 때, 외부 DB에 저장하는 것이 좋지 않을까? 라는 생각도 했다.
Redis Pub/Sub 를 사용하는 것이 옳은 선택인가 ?
팀원분들과 회의를 진행한 결과 Redis를 사용하는 것이 좋을 것 같다는 의견이 최종적으로 결정되었다.
왜냐하면, STOMP는 메시지 브로커를 통해서 메시지를 전달하므로 메시지를 동기적으로 처리한다. 그렇기 때문에 메시지를 받은 후 몽고 DB에 저장하고 응답 받는데 까지 시간이 걸리게 된다.
이러한 방법은 몽고 DB에 대한 디스크 I/O를 포함하기 때문에 I/O지연이 발생할 수 도 있다.
그렇기 때문에, 메시지를 받은 후 몽고 DB에 대한 작업처리를 백 그라운드에서 비동기적으로 처리하는 것이 효율적이라고 판단하였다.
따라서, 비동기적으로 몽고 DB에 저장하는 방법이 더 낮은 latency를 제공할 가능성이 높다고 판단하여 Redis Pub/Sub을 사용하기로 결정하였다.