REDIS 정리

RedisConnectionFactory 역할 : 레디스 서버와의 연결 통로 만드는 역할, 커넥션 풀 같은 거라고 생각하면 된다.

RedisMessageListener Container

: 별도의 스레드에서 동작, 서버가 처음 올라갈 때 설정한 채널에 대해 subcribe 처리

  • 안에 메시지 리스너를 등록하는데 특정 채널의 메시지가 오면 어떤 로직을 실행할지 정의하게 된다. 메시지 리스너의 defaultLisenerMethod는 onMessage이다.

 @Bean
    public RedisMessageListenerContainer redisMessageListenerContainer(
            @Qualifier("chatPubSub") RedisConnectionFactory factory,
            MessageListenerAdapter listenerAdapter
    ) {
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(factory);
        container.addMessageListener(listenerAdapter, new PatternTopic("chat"));
        return container;
    }

package com.example.chatserver.chat.service;

import com.example.chatserver.chat.dto.ChatMessageDto;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.messaging.simp.SimpMessageSendingOperations;
import org.springframework.stereotype.Service;

import java.nio.charset.StandardCharsets;

@Service
public class RedisPubSubService implements MessageListener {

    private static final Logger logger = LoggerFactory.getLogger(RedisPubSubService.class);
    private final StringRedisTemplate stringRedisTemplate;
    private final SimpMessageSendingOperations messageSendingOperations;

    public RedisPubSubService(@Qualifier("chatPubSub") StringRedisTemplate redisTemplate,
                              SimpMessageSendingOperations messageSendingOperations) {
        this.stringRedisTemplate = redisTemplate;
        this.messageSendingOperations = messageSendingOperations;
    }

    /**
     * 메시지를 발행하고, 수신한 구독자 수를 로그로 출력합니다.
     * @param channel 발행할 채널
     * @param message 보낼 메시지
     */
    public void publish(String channel, String message) {
        //execute를 사용하여 네이티브 publish 명령 실행 및 결과 확인
        Long subscriberCount = stringRedisTemplate.execute((RedisConnection connection) ->
                connection.publish(
                        channel.getBytes(StandardCharsets.UTF_8),
                        message.getBytes(StandardCharsets.UTF_8)
                )
        );
        
        logger.info("✅ Message published to channel '{}'. Received by {} subscribers.", channel, subscriberCount);
    }

    @Override
    public void onMessage(Message message, byte[] pattern) {
        try {
            String topic = new String(message.getChannel());
            String payload = new String(message.getBody(), StandardCharsets.UTF_8);

            logger.info("Received message from topic '{}': {}", topic, payload);

            ChatMessageDto chatMessageDto = new ObjectMapper().readValue(payload, ChatMessageDto.class);
            messageSendingOperations.convertAndSend("/topic/" + chatMessageDto.getRoomId(), chatMessageDto);

        } catch (JsonProcessingException e) {
            logger.error("Error processing message on topic", e);
            throw new RuntimeException(e);
        }
    }
}

Last updated