Redis

  • Remote Dictionary Server의 약자다.

  • 이름에 dictionary가 있기 때문에 유추가 가능한데, key-value 형태로 데이터를 저장한다. 여기에 인-메모리(in-memory)이면서 오픈소스라는 특징도 추가로 있다.

  • 여러 용도가 있으나 여기서는 publish/subscribing (Pub/Sub) 메커니즘 용도로 쓰인다.

Publish/Subscribing mechanism

  • 직역하면 출판/구독 메커니즘이다. 유튜브처럼 생각을 하면 이해하기가 쉽다.

  • 먼저 출판자(publisher), 그러니까 메세지를 보내는 측에서 각각 운영하는 채널이 존재한다. 이 때 한 곳에서 여러개의 채널을 운영하는 것도 가능하다. 메세지를 수신하는 측, 즉 구독자(subscriber)은 메세지를 받고 싶은 대상의 채널을 구독하며 (Subscribe), 그 채널에 메세지가 등록이 될 경우 (Publish) Redis에서 이를 직접 채널 구독자들에게 전달을 한다. Redis와 같은 역할을 하는 녀석들을 메세지 브로커 (message broker)이라고 보통 표현한다. 구독자를 대신해서 구독자가 받아야할 메세지, 그리고 어떤 메세지를 우선적으로 전달할지를 정하는 녀석이라고 생각하면 된다.

  • 이 방식의 장점은

    • 구독자와 출판자 사이의 관계가 많이 독립적이라는 것이다. 사실 구독자의 경우 애초에 출판자가 누군지에 대해서도 신경 쓸 필요가 없다. 그냥 자기가 필요한 채널에 대해 구독만 하면 되기 때문. 그리고 물리적으로 독립될 뿐만 아니라 서로 중 하나가 동작되고 있지 않는 와중에도 중간의 채널을 활용해 상호작용이 어느정도 가능하다는 것이 서버-클라이언트 구조 대비 엄청 큰 장점이다.
    • 수천개의 서버를 운용하는 여러 데이터센터가 하나의 pub/sub 체계를 활용하는게 아닌 이상 어지간하면 일반 DB보다 확장성이 더 좋다는 연구 결과가 있다.
  • 이 방식의 단점은

    • 메세지 전송 방식에 대해 신경써야 할 것이 많다는 것이다. 메세지 브로커의 경우 출판자로부터 메세지를 받으면 이를 언제 전달할지, 어떤 우선순위로 전달할지, 몇번 전달할지 등등. 수신 상태 신경 안쓰고 무조건 한번? 아니면 구독자가 받았다는 보장이 될 때까지 전송하도록 여러번? 하지만 받았다는 보장을 받으려면 구독자가 출판 측에 받았다는 수신 신호를 보내야 해서 구독자와 출판자 사이의 독립성이 약해지는 문제도 있다.
    • 아까 위에서 확장성이 좋다고 했는데, 이를 활용하고 있는 출판/구독자 수가 적을 때나 성립하는 얘기다. 즉 pub/sub 네트워크가 소규모여야만 유효하다.
    • 브로커 활용시에 잠재적 보안 문제 존재 등등
  • 좀 더 자세한 내용은 Redis Pub/Sub 관련 doc영문 위키를 참고하자.

Messaging 관련 Spring Data Redis component

  • 구체적인 코드 설명에 들어가기 전에 Spring Data Redis에서 제공하는 component중 이 튜토리얼에서 사용한 것들에 대해 알아보도록 하자.

RedisConnectionFactory

  • documentation

  • 인터페이스다. Thread-safe한 factory로 이름에서 보다시피 Redis connection을 형성하는데 사용된다.

  • 추후 설명할 StringRedisTemplateRedisMessageListenerContainer에서 Redis server과 통신할 연결을 구축하는데 사용되는 녀석이다.

RedisMessageListenerContainer

  • documentation

  • redis message listener을 내포하고 있는 container class이다. 그냥 listener 자체를 사용하지 않고 container로 한겹 감싸는 이유는 listening 과정에서 수행해야 하는 low-level 과정들을 이 녀석이 전부 대신 처리해 줄 수 있기 때문이다.

StringRedisTemplate

  • documentation

  • RedisTemplate의 일종이다. Redis랑 상호작용할 때 보통 String을 가지고 상호작용이 자주 이루어져서 기존 Template보다 좀 더 간소화된, String 기반 상호작용에만 집중하는 class.

MessageListenerAdapter

  • documentation

  • 수신자 (receiver, listener)을 한층 wrapping하는 class인데, 어떤 종류의 메세지가 오느냐에 따라 listener이 각각 어떻게 처리할지를 지정하는데 사용된다. 여기서는 String message만 신경 쓰기 때문에 해당 message만 어떻게 처리할지를 설정을 한다.

  • 사실 여기서 이걸 사용한 이유는, 우리의 receiver class가 추후 container의 addMessageListener에 적합한 object가 아니고 위의 MessageListenerAdapter을 받기 때문인것이 좀 크다.

구현 설명

  • 먼저 위의 RedisConnectionFactory는 Spring에서 bean으로 자동으로 만들며 container method에 주입된다는 점 유의. MessageListenerAdapter도 뭐 listenerAdapter이라는 method의 결과물이 주입될거다. listenerAdapter@Bean이기 때문.

  • listernAdpterReceiverreceiver method의 결과물이 주입될 것이다. receiver@Bean이기 때문.

  • 이제 차근차근 설명하자면, 먼저 listenerAdapter에서 adapter을 생성할때 메세지를 어떻게 처리할지도 설정을 하고 있는데, receiver Object의 receiveMessage method를 사용하도록 설정 중이다. 해당 method는 Receiver class 코드에서 정의했으니 참고.

  • container method의 경우 먼저 새로운 container을 만든 다음에, 거기서 사용할 connection factory를 세팅, 이후 message listening하는 adapter도 세팅한다. 이 때 해당 adapter이 구독할 channel도 설정하는데, PatternTopic class를 사용하는걸 볼 수 있다. Redis pub/sub에서는 pattern subscription도 지원하는데, 해당 pattern을 가지는 channel을 이 listener이 전부 구독하는 기능이다. 다만 여기서는 딱히 그 메커니즘을 활용하진 않아서 그냥 chat이라는 channel에 구독한 상황.

  • 여기까지만 해도 수신은 별 문제없이 이루어진다. 수신을 받으면 LOGGER이 로그에 기록을 남길 것임. 그러나 메세지 송신도 우리는 구현해볼 것이며, 여기서 StringRedisTemplate이 활용된다.

  • 일단 template method는 그냥 StringRedisTemplate을 하나 생성한다.

  • 그러면 현재 application context상에 정의되어 있는 bean은

    • Receiver bean
    • MessageListenerAdapter bean
    • RedisMessageListenerContainer bean
    • StringRedisTemplate bean 이 존재.
  • main을 이제 보도록 하자. 기존과 다르게 run이후에 거기서 return한 context를 저장해두는 것을 볼 수 있다. 여기서 해당 application context상의 bean들을 접근하는 것이 가능하다. 즉 위의 bean들 접근이 가능하다는 것이다.

  • 이 확보한 bean들로 메세지 송신을 해볼것이다. 보면 송신 기준이 현재 수신 메세지 확인했을 때 본인이 받은 메세지가 없을 때이며, 먼저 LOGGER에다가 본인이 메세지를 송신한다는 것을 남기고 StringRedisTemplate의 instance인 template을 통해 메세지를 보낸다. convertAndSend method는 단순히 Hello from Redis!라는 메세지를 chat이라는 채널에 보내는 작업을 한다. 이후 0.5초동안 기다린다.

  • 메세지를 받은 것을 확인하면 그냥 즉시 종료됨.

결과물

  • 직접 시현했을 때 나온 로그
2024-01-09T13:54:52.340+09:00  INFO 50272 --- [           main] o.e.m.MessagingRedisApplication          : No active profile set, falling back to 1 default profile: "default"
2024-01-09T13:54:52.619+09:00  INFO 50272 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Multiple Spring Data modules found, entering strict repository configuration mode
2024-01-09T13:54:52.621+09:00  INFO 50272 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data Redis repositories in DEFAULT mode.
2024-01-09T13:54:52.640+09:00  INFO 50272 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 8 ms. Found 0 Redis repository interfaces.
2024-01-09T13:54:53.447+09:00  INFO 50272 --- [           main] o.e.m.MessagingRedisApplication          : Started MessagingRedisApplication in 1.375 seconds (process running for 1.962)
2024-01-09T13:54:53.450+09:00  INFO 50272 --- [           main] o.e.m.MessagingRedisApplication          : Sending message...
2024-01-09T13:54:53.462+09:00  INFO 50272 --- [    container-1] org.example.messagingredis.Receiver      : Received <Hello from Redis!>
profile
안 흔하고 싶은 개발자. 관심 분야 : 임베디드/컴퓨터 시스템 및 아키텍처/웹/AI

0개의 댓글