Redis를 활용해 message publish/message subscribing를 구현해볼 것이다.
Remote Dictionary Server의 약자다.
이름에 dictionary가 있기 때문에 유추가 가능한데, key-value 형태로 데이터를 저장한다. 여기에 인-메모리(in-memory)이면서 오픈소스라는 특징도 추가로 있다.
여러 용도가 있으나 여기서는 publish/subscribing (Pub/Sub) 메커니즘 용도로 쓰인다.
직역하면 출판/구독 메커니즘이다. 유튜브처럼 생각을 하면 이해하기가 쉽다.
먼저 출판자(publisher), 그러니까 메세지를 보내는 측에서 각각 운영하는 채널이 존재한다. 이 때 한 곳에서 여러개의 채널을 운영하는 것도 가능하다. 메세지를 수신하는 측, 즉 구독자(subscriber)은 메세지를 받고 싶은 대상의 채널을 구독하며 (Subscribe), 그 채널에 메세지가 등록이 될 경우 (Publish) Redis에서 이를 직접 채널 구독자들에게 전달을 한다. Redis와 같은 역할을 하는 녀석들을 메세지 브로커 (message broker)이라고 보통 표현한다. 구독자를 대신해서 구독자가 받아야할 메세지, 그리고 어떤 메세지를 우선적으로 전달할지를 정하는 녀석이라고 생각하면 된다.
이 방식의 장점은
이 방식의 단점은
좀 더 자세한 내용은 Redis Pub/Sub 관련 doc과 영문 위키를 참고하자.
인터페이스다. Thread-safe한 factory로 이름에서 보다시피 Redis connection을 형성하는데 사용된다.
추후 설명할 StringRedisTemplate
과 RedisMessageListenerContainer
에서 Redis server과 통신할 연결을 구축하는데 사용되는 녀석이다.
redis message listener을 내포하고 있는 container class이다. 그냥 listener 자체를 사용하지 않고 container로 한겹 감싸는 이유는 listening 과정에서 수행해야 하는 low-level 과정들을 이 녀석이 전부 대신 처리해 줄 수 있기 때문이다.
RedisTemplate
의 일종이다. Redis랑 상호작용할 때 보통 String
을 가지고 상호작용이 자주 이루어져서 기존 Template보다 좀 더 간소화된, String
기반 상호작용에만 집중하는 class.
수신자 (receiver, listener)을 한층 wrapping하는 class인데, 어떤 종류의 메세지가 오느냐에 따라 listener이 각각 어떻게 처리할지를 지정하는데 사용된다. 여기서는 String
message만 신경 쓰기 때문에 해당 message만 어떻게 처리할지를 설정을 한다.
사실 여기서 이걸 사용한 이유는, 우리의 receiver
class가 추후 container의 addMessageListener
에 적합한 object가 아니고 위의 MessageListenerAdapter
을 받기 때문인것이 좀 크다.
먼저 위의 RedisConnectionFactory
는 Spring에서 bean으로 자동으로 만들며 container
method에 주입된다는 점 유의. MessageListenerAdapter
도 뭐 listenerAdapter
이라는 method의 결과물이 주입될거다. listenerAdapter
이 @Bean
이기 때문.
또 listernAdpter
의 Receiver
도 receiver
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
beanMessageListenerAdapter
beanRedisMessageListenerContainer
beanStringRedisTemplate
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!>