Computer >> 컴퓨터 >  >> 프로그래밍 >> Redis

Redis Pub/Sub 입문 가이드: 발행/구독 패턴의 핵심 이해하기

Publish/Subscribe(pub/sub, 발행/구독)는 수십 년간 사용되어 온 소프트웨어 엔지니어링 패턴이지만, 실제로 활용하려면 별도의 메시징 서버와 전문 지식이 필요한 경우가 많았습니다.

Redis pub/sub은 발행/구독 패턴을 간결하고 가볍게 구현한 기능으로, Redis 2.0 릴리스 이후 모든 Redis 서버에 기본 포함되어 있습니다. 덕분에 Redis가 배포된 곳이라면 어디서든 손쉽게 사용할 수 있으며, Redis 하나만으로도 빠르고 단순한 pub/sub 시스템을 신속하게 구축할 수 있습니다.

Redis pub/sub이 애플리케이션에 적합한 선택인지 판단하려면 먼저 pub/sub의 설계 목적과 목표를 이해하고, 이후 Redis pub/sub 구현체의 특성을 살펴보는 것이 중요합니다.

Pub/Sub 개요

Pub/Sub 설계: '발행자'와 '구독자'의 분리

확장 가능한 소프트웨어를 설계할 때 핵심 목표는 의존성을 줄이고 정보를 모듈별로 분리하는 것입니다. 발행/구독 메시징 패턴은 소프트웨어에서 메시지를 발행하는 부분과 메시지를 받아 동작을 수행하는 부분을 서로 분리합니다. 이해를 돕기 위해 몇 가지 예시를 살펴보겠습니다.

'발행자'의 예시

  • 채팅방에 메시지를 전송하는 애플리케이션
  • 자신의 상태나 제공 서비스를 알리는 Docker 컨테이너
  • 주식 거래 체결 가격을 알리는 애플리케이션
  • 측정값을 전송하는 가정용 온도 센서
  • 멀티유저 게임의 메시지 브로드캐스터("Elon이 그루에게 잡아먹혔습니다!")

'구독자'의 예시

  • 채팅방에서 메시지를 수신 대기하는 애플리케이션
  • 알림을 Slack으로 전달하는 애플리케이션
  • 실시간 상황을 확인하는 모바일 클라이언트
  • 이벤트를 기록해 추후 분석에 활용하는 로깅 서비스

pub/sub 패턴을 활용하면 발행자를 개발할 때 구독자를 깊이 고민할 필요가 없고, 반대의 경우도 마찬가지입니다. 위 예시에서 하나의 발행자가 보내는 메시지에 여러 구독자가 관심을 가질 수 있으며, 구독자들은 심지어 모든 발행자의 메시지를 수신하고 싶어할 수도 있습니다.

pub/sub에서 발행자는 자신의 구독자에 대해 알 필요가 없습니다. 발행자는 메시지를 채널(channel)(다른 시스템에서는 흔히 토픽(topic)이라 부름)로 푸시한 뒤 곧바로 다음 작업으로 넘어갑니다. 해당 채널을 수신 대기 중인 구독자들이 있다면, 메시지가 발행된 순간 이를 받게 됩니다.

구독자는 하나 이상의 채널을 수신 대기하며, 들어오는 메시지에 반응하도록 설계됩니다. 만약 구독자가 발행되는 메시지 속도를 따라가지 못하면 일부 메시지를 놓치게 됩니다. 이는 유용한 설계 방식입니다. 느린 구독자의 처리 용량과 무관하게 시스템 전체가 확장될 수 있기 때문입니다. 발행자는 구독자의 상태에 발목 잡히지 않고 신속하게 다음 작업으로 진행합니다.

Pub/Sub의 목표: 작업량이 아닌 '전달'의 확장

pub/sub은 소프트웨어 확장에 사용되는 패턴이므로, 어떤 종류의 확장에 도움이 되는지 이해하는 것이 중요합니다. 여기서 pub/sub과 메시지 큐잉(queueing)의 차이를 구분하는 것이 핵심입니다.

큐잉 패턴에서는 큐(Redis에서는 리스트(list))가 처리 대기 메시지를 버퍼링하고, 워커(worker) 풀이 리스트에서 항목을 꺼내 처리합니다. 이 모델에서는 각 메시지가 오직 한 명의 워커에게만 전달되므로, 워커 풀의 규모를 늘리면 큐 처리 속도가 그만큼 향상됩니다. 모든 워커는 주어진 메시지를 동일한 방식으로 처리합니다.

반면 pub/sub에서는 시스템이 채널의 모든 메시지를 해당 채널의 모든 구독자에게 전달하려고 시도합니다. 이는 다대다(many-to-many) 패턴으로, 각기 다른 구독자들이 같은 메시지를 저마다의 방식으로 처리합니다. 하나는 내구성 있는 로그에 기록하고, 다른 하나는 Slack 채널로 전송하며, 또 다른 하나는 영업 사무소의 벨을 울리는 식입니다.

요약하면, pub/sub은 메시지 전달(delivery)을 확장하고, 큐잉은 메시지 작업량 처리를 확장합니다. Redis는 이 두 가지 목적 모두에 널리 사용되고 있습니다. Redis 기반 큐잉의 대표적인 사례로 Sidekiq를 참고해 보세요.

Redis pub/sub의 특징

pub/sub은 오랫동안 존재해 온 패턴으로, 핵심 개념은 동일하지만 세부 기능은 구현체마다 상당히 다릅니다.

Redis pub/sub은 가볍고 빠른 구현체입니다. 그 설계를 잘 이해하려면 Redis pub/sub에 포함되어 있지 않은 기능들을 살펴보는 것이 도움이 됩니다.

  • 영속성(persistence) 또는 값 캐싱 없음
  • 전달 보장 없음
  • 클러스터 최적화 미지원… 아직은

영속성 없음: "채팅방 같다고 하지 않았나요?"

디스크에 기록될 수 있는 대부분의 Redis 연산과 달리, Redis pub/sub은 비영속적(non-persistent)입니다. 발행된 메시지는 구독자에게 직접 전달된 후 즉시 폐기되며, Redis의 메모리나 디스크에는 어떠한 기록도 남지 않습니다.

Redis pub/sub이 채팅방 구현에 자주 사용된다는 이야기를 듣고 혼란스러워하는 초보 사용자들이 종종 있습니다. 많은 사람들이 Slack 같은 도구를 채팅룸으로 인식합니다. 로그인하면 최근 메시지를 확인할 수 있고, 이후 새 메시지를 모두 받아볼 수 있죠. 하지만 사실 '최근 메시지 보기'는 pub/sub의 기능이 전혀 아니며, 별도의 수단으로 처리해야 합니다. pub/sub은 새 메시지의 전달만 담당합니다. 이런 의미에서 pub/sub은 라이브 스트리밍과 유사합니다. 스트림을 켜는 순간부터 정보를 받기 시작하지만, 켜기 이전에 무슨 일이 있었는지는 알 수 없습니다. 참고로 인터넷 릴레이 챗(IRC) 역시 내장된 히스토리나 메시지 보관 기능 없이 pub/sub 모델을 사용합니다. 채팅방 은유가 널리 쓰이는 이유가 바로 여기에 있습니다.

그럼에도 불구하고 Redis와 pub/sub으로 채팅방을 구현하는 것은 충분히 가능합니다. 이를 위해서는 메시지를 발행할 뿐만 아니라 리스트(list)에도 함께 push하여, 사용자가 메시지 히스토리를 조회할 수 있도록 해야 합니다.

전달 보장 없음

pub/sub 모델에서 구독자가 메시지를 반드시 수신한다는 보장은 없습니다. 구독자에게 네트워크 문제가 발생했거나, 메시지를 충분히 빠르게 읽지 못했거나, 기타 사유로 메시지 발행 시점에 연결되어 있지 않은 것으로 판단되면 해당 메시지는 그냥 수신되지 않습니다. 발행자는 구독자가 아무도 없더라도 채널로 메시지를 보낼 수 있으며, 그런 메시지는 그대로 폐기됩니다.

일부 다른 메시징 시스템은 읽음 확인(receipt), 'ack', 또는 구독자별 버퍼 저장 등을 통해 짧은 연결 끊김에 대비합니다. Redis는 여기서 단순한 방식을 선택했습니다. 놓친 메시지는 놓친 것이며, 반드시 수신자에게 도달해야 하는(must) 메시지는 다른 수단으로 전달해야 합니다.

이러한 트레이드오프는 실제보다 나빠 보일 수 있습니다. 메시지 확인 응답과 구독자별 버퍼링을 생략함으로써 Redis pub/sub은 메시지를 매우 빠르게 처리할 수 있으며, 평상시의 안정적인 메시지 전달만으로 충분한 시스템도 많습니다.

비효율적인 클러스터 확장, 당분간은…

pub/sub은 확장을 위한 솔루션이므로, 메시지뿐만 아니라 메시지를 전달하는 서비스 자체의 확장성도 고려해야 합니다. RabbitMQ와 Kafka를 포함한 많은 메시징 시스템은 메시징 서비스 규모가 커져도 고가용성과 합리적인 확장 특성을 유지하도록 설계되었습니다.

Redis Cluster(Redis 3.0부터 포함)는 Redis pub/sub을 기본적으로 지원하지만, 중요한 단서가 있습니다. 발행된 모든 메시지가 Redis 클러스터의 모든 멤버에게 브로드캐스트되므로, 클러스터 규모가 커지면 트래픽으로 금세 감당하기 어려워질 수 있습니다.

이에 대한 현실적인 해법은, pub/sub을 개별 Redis 서버 또는 소규모 pub/sub 전용 클러스터에서만 사용하는 것입니다. 두 방식 모두 대량의 메시지를 처리할 수 있습니다. 장기적으로는 Redis Cluster에 필요한 노드로만 메시지를 라우팅하는 더 스마트한 기능이 추가될 예정이지만, 해당 기능은 아직 설계 단계에 있습니다.

마무리

전반적으로 Redis pub/sub은 소프트웨어 확장에 유용한 도구입니다. 위에서 살펴본 각 기능의 부재는 모두 설계상의 트레이드오프로, 모든 메시징 요구를 충족하는 만능 해법이 되는 대신 단순함과 속도를 얻은 결과입니다.

Redis의 다른 많은 부분과 마찬가지로, 모든 전문적인 사용 사례에 맞춰져 있지는 않지만 조금만 이해하면 오랫동안 유용하게 활용할 수 있습니다. Redis의 다른 기능들과 결합하면 개발자의 도구 벨트에서 매우 강력한 무기가 됩니다.

여기까지 개요를 마쳤으니, 이제 실제 pub/sub 코드 예시를 살펴볼 준비가 되었을 겁니다. 다음으로 'Redis Pub/Sub: How To'를 확인해 보세요.