채팅 프로젝트를 진행하던 도중 WebSocket Session을 효율적으로 관리하려면 어떻게 해야할까? 에 대한 질문을 공부하며
이를 코드로 작성하였고 본 포스팅을 작성하게 되었습니다.
서버와 사용자가 WebSocket 연결이 되어있을 때, 사용자가 정상적으로 연결을 끊는다면 서버도 이를 파악하고 정상적으로 사용자와의 연결을 끊을 수 있습니다.
하지만 사용자 측에서 비정상적으로 연결이 끊긴다면 서버가 사용자가 연결이 되어있는지 안되어있는지 판단하지 못해 연결되어 있다고 판단할 수 있습니다.
이번 포스트에선 이를 보완하기 위해 서버와 사용자의 WebSocket 연결이 의도치 않게 끊겼을 때,
Ping / Pong 으로 서버가 사용자와의 WebSocket 연결 상태를 확인하는 코드를 작성해 보겠습니다.
Ping / Pong
본 포스트에선 서버에서 Ping 메시지를 보내는 것을 기준으로 합니다.
서버에서 Ping을 주기적으로 보내주면 사용자는 Pong을 서버에게 전송하여 연결되어 있음을 서버에게 알려줍니다.
그렇다면 서버가 Ping을 보냈을 때 Pong이 얼마나 오랫동안 오지 않으면 끊겼다고 판단해야 하는가? 에 대한 답은 없으며 자기가 개발하고 있는 도메인 지식에 따라야 한다고 합니다.
Spring에서 Ping / Pong
Spring에선 서버에서 Ping 타입의 메시지를 보내면 WebSocket으로 연결된 사용자는 Pong 타입의 메세지를 응답하게 됩니다.
저는 서버에서 1초마다 연결된 WebSocketSession에게 Ping을 보내도록 코드를 작성하였습니다.
즉, 서버에서 Ping 타입의 메시지를 보낼 땐 아래의 코드를 사용합니다.
PingMessage를 보내면 , 개발자가 코드를 직접 작성하지 않아도 사용자와 정상적으로 연결되어 있다면 PongMessage를 받게 됩니다.
정상적으로 연결이 되어있다면, 서버는 PongMessage를 받고, 비정상적으로 연결이 끊겨있다면 서버는 PongMessage를 받지 못하게 됩니다.
따라서 개발자는 이를 처리하기 위해 다양한 방식으로 비정상적으로 연결이 끊긴 WebSocket Session을 관리할 수 있는데, 제가 참고한 블로그에선 Session Time을 재지정하는 방식으로 하였고, 저는 PongMessage를 4번 이상 받지 못하면 WebSocket Session이 비정상적으로 연결이 끊겼다고 판단하여 WebSocketSession을 close 하는 방식으로 진행하였습니다.
이를 코드로 구현하기 위해
기존에 WebSocketSession을 관리하는 sessions
서버가 Ping을 보냈을 때 Pong을 정상적으로 받았는지를 체크하는 pongReceivedSessions 변수를 선언하였습니다.
1초마다 서버에서 Ping을 보내기
Ping 메시지를 1초마다 보내기 위해 @Scheduled( fixedRate = 1000 )로 설정하였습니다.
1초마다 서버와 연결된 사용자에게 PingMessage를 보내게 됩니다. 이때 Pong 메시지를 받았는지 확인하는 pongReceivedSessions에서 pongCount를 하나씩 내려줍니다.
PongMessage를 받았을 때
정상적으로 서버가 Pong Message를 받게 되면 pongCount를 초기상태로 재설정하게 됩니다.
서버가 4번 연속 PongMessag를 받지 못했을 때
서버가 PongMessage를 받지 못하게 된다면 pongReceivedSession을 갱신하지 못하게 되어 pongCount 값이 0이 되게 됩니다.
이는 서버가 PingMessage를 4번이나 보냈는데 PongMessage을 연속으로 4번이나 보내지 못했다고 생각하여 연결을 끊어줍니다.
비정상적으로 연결이 끊긴 WebSocketSession을 정상적으로 사용자와의 연결이 끊김을 파악하고, 사용자와의 연결을 끊고 afterConnectionClosed 함수를 통해 서버가 사용자와의 연결이 끊긴 후를 처리하도록 해줍니다.
마치며
이번 포스트에선 서버와 사용자 연결이 비정상적으로 끊겼을 때, 서버가 이를 Ping / Pong을 통해 사용자와 연결이 비정상적으로 연결이 끊김을 파악하고, 서버가 사용자와의 연결을 끊어주는 코드를 작성해 보았습니다.
하지만, 저는 현업에서 직접 도메인과 관련된 WebSocket을 작성해 본 적도 없고, 위 코드가 시간복잡도 측면에서 비효율적일 가능성이 매우 높습니다...
본 포스트에선 Spring에서 Ping/ Pong 메시지를 서버에서 보내고 사용자가 Pong 메세지를 받는지 안 받는지가 주된 포스팅이며,
이를 처리하는 과정은 저와 같이 작성하지 않으셔도 됩니다.
그리고 STOMP에서 서버와 연결된 사용자 Session List를 얻는 방법을 아시는 분은 댓글로 알려주시면 감사하겠습니다.
이와 관련하여 제가 작성한 질문입니다.
spring boot - How can I get STOMP Connected Sessions List? - Stack Overflow
참고
Spring WebSocket Ping/Pong (brunch.co.kr)
'스프링' 카테고리의 다른 글
분산 시스템에서 메시지 안전하게 다루기 (0) | 2023.12.22 |
---|---|
쿠폰 발급 동시성 제어하기, 성능테스트로 성능 개선하기 (0) | 2023.11.29 |
스케줄러와 Transactional 테스트 코드에서 생긴 문제 해결하기 (0) | 2023.10.09 |
WAS, Servlet 용어 정리 (0) | 2023.01.04 |
Controller , Service , Repository 이해하기 (0) | 2022.05.19 |