Redis의 pub/sub
Publish / Subscribe 란 특정한 주제(topic)에 대하여 해당 topic을 구독한 모두에게 메시지를 발행하는
통신 방법으로 채널을 구독한 수신자(클라이언트) 모두에게 메세지를 전송 하는것을 의미한다.
하나의 Client가 메세지를 Publish하면, 이 Topic에 연결되어 있는 다수의 클라이언트가
메세지를 받을 수 있는 구조이다.
Springboot에서 redis의 pub/sub구현하기
RedisConfig
@Configuration
public class RedisConfig {
@Value("${spring.redis.host}")
private String redisHost;
@Value("${spring.redis.port}")
private int redisPort;
@Bean
public RedisConnectionFactory redisConnectionFactory(){
return new LettuceConnectionFactory(redisHost,redisPort);
}
// 저장하려는 자료 형태에 따라서 RedistTemplate을 추가해주면 된다.. 그리고 사용하려는 곳에서 @Autowired하면 됨
@Bean
public RedisTemplate<String, Object> redisTemplate() {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory());
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(String.class));
//Message를 String으로...
return redisTemplate;
}
/**
* redis pub/sub 메시지를 처리하는 listener 설정
*/
@Bean
public RedisMessageListenerContainer redisMessageListener(RedisConnectionFactory connectionFactory) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
return container;
}
}
redis의 MessageListener가 한명의 subscriber가 되고
이를 저장하는 Container를 RedisMesageListenrContainer라고 한다.
Test
@SpringBootTest
class PracPubSubTests {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private RedisMessageListenerContainer redisMessageListenerContainer;
//MessageListener를 저장한 container, 여기에 channel과 구독자를 설정할 수 있따.
private final ObjectMapper mapper = new ObjectMapper();
@Test // 방에 추가된 사람마다 System.out.println()이 잘 됐는지만 확인하겠습니다.
public void pubSubTest(){
ChannelTopic topic1=new ChannelTopic("topic1");
MessageListener subscriber1=new MessageListener() {
@Override
public void onMessage(Message message, byte[] pattern) {
try {
String publishedMessage = mapper.readValue(message.getBody(), String.class);
System.out.println("I'm subscriber1 and the message i received is : '" + publishedMessage +"'");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
};
MessageListener subscriber2=(message,pattern)->{
try{
String publishedMessage = mapper.readValue(message.getBody(), String.class);
System.out.println("I'm subscriber2 and the message i received is : '" + publishedMessage + "'");
}catch (IOException e) {throw new RuntimeException(e);}
};
//topic1에 2명의 subscriber 등록
redisMessageListenerContainer.addMessageListener(subscriber1,topic1 );
redisMessageListenerContainer.addMessageListener(subscriber2,topic1);
redisTemplate.convertAndSend(topic1.getTopic(), " publish Message ");
ChannelTopic topic2=new ChannelTopic("topic2");
redisTemplate.convertAndSend(topic2.getTopic()," There is no subscriber");
}
}
redisMessageListnerContainer에 topic1과 함께 subscriber1,2를 등록했다.
topic1 채널을 통해 메세지를 publish하면 subscriber1,2는 메세지를 수신받고 onMessage()를 실행한다.
topic2로 publish해도 수신자가 없기때문에 아무일도 일어나지 않는다.
실행결과
subscriber1,2간의 순서는 보장하지 않는다.
※publisher는 따로 없고 convertAndSend()메소드를 통해 topic(channel)만 지정해주면 된다.
다른 블로그 글에서는 의미를 명확히 하기위해서 Publisher라는 이름의 클래스를 따로 만드는 경우가 많다.