안녕하세요! 반갑습니다!
이번 포스팅에서는 채팅 메세지 전송 속도를 어떻게 개선했는지 자세히 설명해보고자 합니다!
자세한 코드는 깃허브에서 확인하실 수 있습니다!
https://github.com/HyemIin/Catchroom_Chat
또한 기초적인 채팅 아키텍처 설명은 아래 포스팅을 참고해주세요!
1. 뭐할꺼야?(What?)
앞서 말한대로, 채팅 메세지 전송 속도를 개선하고자 합니다!
2. 왜 해?(Why?)
기존 채팅 전송 속도는 약 0.1초(100ms)로 느리진 않지만 최선은 아닌 상태였습니다. 채팅 시스템을 사용하는데 큰 불편함은 없었지만,
저희는 이 부분이 좀 더 개선할 수 있는 부분이라 판단했습니다.
보다 효율적인 시스템을 구축할 수 있을 것이라는 기대감이 들었고, 그 즉시 저희는 개선 방안을 구체적으로 수립해나갔습니다.
3. 어떻게 구현했어??(How?)
1) 이전 구현 방식(Before)
이전 채팅 메세지를 전송하는 방법은 아래와 같았습니다.
먼저 사용자가 채팅 메세지를 보내면, 채팅 서버는 @MessageMapping 어노테이션이 달린 채팅 컨트롤러에서 메세지를 받습니다.
해당 메세지를 몽고 디비에 저장하고, feign client를 통해 메인서버에서 현재 사용자가 참여중인 채팅방 리스트를 조회하여 가져옵니다.
(이 부분이 주요 개선 파트입니다!!)
MySQL에 저장되어있는 전체 채팅방 리스트 중에서 쿼리를 통해 사용자의 채팅방 리스트를 필터링하여 채팅 서버로 가져온 후, 채팅 서버 내부 로직을 통해 현재 메세지가 발송된 채팅방의 최신 메세지를 업데이트 해줍니다.
마지막으로 채팅방 리스트를 마지막 메세지 시간순으로 정렬하여 발송한 메세지와 함께 DTO에 담아 redis publish 해줍니다.
2) 이전 방식의 문제점
위와 같은 방식은 feign client를 통해 메인서버와 연결되고, 다시 MySQL 서버까지 접속하여 쿼리를 통해 사용자가 참여중인 채팅방 리스트를 필터링하여 채팅방 리스트를 만들어냅니다.
기능의 특징 상 특정 연결 지점에서 문제가 생기면 바로 기능 장애로 이어지는데, 이렇게 연결고리가 많다는 의미는 즉 문제가 생길 수 있는 포인트가 많다는 뜻으로 서비스 안정성이 떨어진다고 이해했습니다.
또한 feign client를 이용하여 메인 서버와 통신하는 것은 휼륭한 방법 중 하나지만, 네트워크 트래픽이나 기타 환경에 따라 속도의 차이가 있을 수 있다는 점, 기능의 프로세스가 너무 길어 다소 시간이 소요된다는 점 역시 개선이 필요한 부분이라 생각했습니다.
3) 변경 구현 방식(After)
위와 같은 문제점을 개선하고자 기존 feign client를 이용해 최신 채팅방 리스트를 불러오는 로직을 Redis에서 최신 채팅방 리스트를 불러오는 로직으로 변경했습니다.
이렇게 변경하고 나니 시간과 안정성 측면에서 훨씬 이점이 있었습니다.
먼저 기본 프로세스에서 feign client를 이용하는 로직이 사라지니 채팅방 리스트를 불러올 때 굳이 메인 서버까지 접근하지 않아도 되므로 기능 프로세스의 길이가 줄어들었습니다. 이로 인해 보다 시스템의 안정성을 확보할 수 있었습니다.
또한 채팅 전송 속도에서 이점도 있었습니다!!
메세지 전송 속도의 경우 기존 방식에서 약 0.1초(100ms) 정도의 전송 시간이 소요되었으나, Redis를 적용하여 채팅방 리스트를 미리 저장하는 로직을 도입한 후 약 0.008초(8ms) 정도의 전송 시간만 소요된 것을 확인할 수 있습니다!
물론 위와 같은 방식으로 변경하면서 레디스를 위한 로직이 추가될 수 밖에 없었습니다.
또한 레디스에 저장하기 위해선 최초 1번은 무조건 feign client를 이용하여 메인 서버에서 채팅방 리스트 정보를 가져와야한다는 한계가 존재합니다.(아래 코드 참고)
그럼에도 불구하고 레디스를 도입함으로써 채팅방 리스트를 불러오는 시간을 확 줄일 수 있었기에 이는 의미있는 발전이라 생각합니다!ㅎㅎ
또한 아이러니하게도 기존 포스팅했던 "채팅방 리스트 속도 개선" 방법에서 더 나아가 개선하게되었습니다(?)ㅋㅋㅋㅋㅋ
아무래도 채팅을 publish할 때 requestDTO에 채팅방 리스트를 담아서 보내다 보니 개선 포인트가 겹쳐 일석이조가 되었습니다!
앞으로는 앞서 나온 한계점을 바탕으로 더 효율적인 로직을 구성할 수 있을지 차차 고민해나가겠습니다.
아래는 현재 코드입니다.
<ChatController> - Chat Server
@MessageMapping("/chat/message")
public void message(ChatMessageDto message,
@Header("Authorization") String accessToken
) {
ChatMessageDto chatMessageDto = chatMongoService.save(message);
chatService.sendChatMessage(chatMessageDto, accessToken);
}
<ChatServce> - Chat Server
public void sendChatMessage(ChatMessageDto chatMessage, String accessToken) {
Long userId = chatMessage.getUserId();
Long partnerId;
// 채팅방이 삭제되는 것이라면 채팅방을 delete 해준다.
if (chatMessage.getType().equals(MessageType.DELETE)) {
chatRoomService.deleteChatRoom(accessToken, chatMessage.getRoomId(), userId);
}
// 채팅방 리스트에 새로운 채팅방 정보가 없다면, 넣어준다. 마지막 메시지도 같이 담는다. 상대방 레디스에도 업데이트 해준다.
ChatRoomGetResponse newChatRoom = null;
if (chatRoomRedisRepository.existChatRoom(userId, chatMessage.getRoomId())) {
newChatRoom = chatRoomRedisRepository.getChatRoom(userId, chatMessage.getRoomId());
} else {
newChatRoom = chatRoomService.getChatRoomInfo(accessToken, chatMessage.getRoomId());
}
partnerId = getPartnerId(chatMessage, newChatRoom);
setNewChatRoomInfo(chatMessage, newChatRoom);
// 마지막 메시지들이 담긴 채팅방 리스트들을 가져온다. // 파트너 채팅방 리스트도 가져온다. (파트너는 userId 로만)
List<ChatRoomGetResponse> chatRoomList = chatRoomService.getChatRoomList(userId, accessToken);
List<ChatRoomGetResponse> partnerChatRoomList = getChatRoomListByPartnerId(partnerId);
// 마지막 메세지 기준으로 정렬 채팅방 리스트 정렬
chatRoomList = chatRoomService.sortChatRoomListLatest(chatRoomList);
partnerChatRoomList = chatRoomService.sortChatRoomListLatest(partnerChatRoomList);
MessageSubDto messageSubDto = MessageSubDto.builder()
.userId(userId)
.partnerId(partnerId)
.chatMessageDto(chatMessage)
.list(chatRoomList)
.partnerList(partnerChatRoomList)
.build();
redisPublisher.publish(messageSubDto);
}
감사합니다.
'Development > Spring&Springboot' 카테고리의 다른 글
스프링 빈이란?? (3) | 2024.02.28 |
---|---|
스프링 컨테이너 DI와 IoC (2) | 2024.02.28 |
[채팅] 채팅방 리스트 최신화 속도 개선 (3) | 2024.02.09 |
SpringBoot 내 html 파일을 어떻게 불러오는지 모르겠다면 (0) | 2023.07.04 |
[JPA] 스프링 관련 어노테이션 (0) | 2023.03.06 |