웹 소캣 실시간 채팅(2)

InSeok·2023년 2월 1일
0

TIL

목록 보기
51/51

여러대의 채팅 서버간에 메시지 공유하기(feat. Redis)

앞장에서 구현한 채팅서버 문제점

  1. 채팅방의 메인 저장소 부재로 서버의 메모리에 적재된 채팅방은 서버를 재시작할 때마다 초기화 되는 이슈 발생

    • DB를 이용하거나 다른 저장소를 이용하여 채팅방이 계속 유지되도록 처리가 필요 ⇒ Redis 사용
  2. 채팅서버가 여러개일 경우 서버간 채팅 공유 불가

    • 현재는 pub/sub가 발생한 서버 내에서만 메시지를 주고 받을수 있다.
    • 구독대상인 채팅방(topic)이 생성된 서버 안에서만 유효하므로 다른 서버로 접속한 클라이언트는 해당 채팅방이 보이지 않고, 채팅방 구독도 불가능함.
    • 구독대상이 여러 서버에서 접근할 수 있도록 개선 필요

    ⇒ 공통으로 사용가능한 pub/sub 시스템을 구축하고 모든 서버들이 해당 시스템을 통해 pub/sub 메시지를 주고받도록 변경해야 한다. (Redis 활용)

    pub/sub 구조

    출처: https://medium.com/frientrip/pub-sub-잘-알고-쓰자-de9dc1b9f739

Redis 발행/구독 모델 구현을 위한 서비스 생성

  • Redis에서는 공통 주제(Topic)에 대하여 구독자에게 메시지를 발행 할 수 있는 기능이 있다.
  • Redis의 topic을 채팅방으로 가정하고, pub/sub를 메시지를 보내고 수신하는 행위로 간주한다.

Redis 발행 서비스 구현

  • 채팅방에 입장하여 메시지를 작성하면 해당 메시지를 Redis Topic에 발행하는 기능의 서비스.
  • 이 서비스를 통해 메시지를 발행하면 대기하고 있던 redis 구독 서비스가 메시지를 처리한다.

@RequiredArgsConstructor
@Service
public class RedisPublisher {
    private final RedisTemplate<String, Object> redisTemplate;

    public void publish(ChannelTopic topic, ChatMessage message) {
        redisTemplate.convertAndSend(topic.getTopic(), message);
    }
}

Redis 구독 서비스 구형

  • Redis에 메시지 발행이 될때까지 대기하였다가 메시지가 발행되면 해당 메시지를 읽어 처리하는 리스너
  • MessageListener를 상속받아 onMessage 메서드를 재작성한다.
  • Redis에 메시지가 발행되면 해당 메시지를 ChatMessage로 변환하고 messaging Template을 이용하여 채팅방의 모든 websocket 클라이언트 들에게 메시지를 전달한다.
@Slf4j
@RequiredArgsConstructor
@Service
public class RedisSubscriber implements MessageListener {

    private final ObjectMapper objectMapper;
    private final RedisTemplate redisTemplate;
    private final SimpMessageSendingOperations messagingTemplate;

    /**
     * Redis에서 메시지가 발행(publish)되면 대기하고 있던 onMessage가 해당 메시지를 받아 처리한다.
     */
    @Override
    public void onMessage(Message message, byte[] pattern) {
        try {
            // redis에서 발행된 데이터를 받아 deserialize
            String publishMessage = (String) redisTemplate.getStringSerializer().deserialize(message.getBody());
            // ChatMessage 객채로 맵핑
            ChatMessage roomMessage = objectMapper.readValue(publishMessage, ChatMessage.class);
            // Websocket 구독자에게 채팅 메시지 Send
            messagingTemplate.convertAndSend("/sub/chat/room/" + roomMessage.getRoomId(), roomMessage);
        } catch (Exception e) {
            log.error(e.getMessage());
        }
    }
}

ChatRoomRepository

  • 채팅방 정보는 초기화 되지 않도록 생성 시 Redis Hash에 저장하도록 처리.
  • 채팅방 정보를 조회할 때는 Redis Hash에 저장된 데이터를 불러온다.
  • 채팅방 입장시에는 채팅방 id로 Redis topic을 조회하여 pub/sub 메시지 리스너와 연동한다.
profile
백엔드 개발자

0개의 댓글