연결 정리는 finally에서 「어떻게 끝나든 정확히 한 번」
프레임워크의 disconnect 콜백은 예외로 끝나면 불리지 않을 수 있다. 진입점을 try/finally로 감싸고 두 번 정리하지 않게 표시를 둔다.
웹소켓 서버에서 같은 구성의 프로세스끼리 메모리와 접속 수가 점점 벌어진다. 클라이언트는 이미 떠났는데 서버에는 그 연결의 그룹 구독과 상태가 남아 있다. 좀비 연결이다.
원인은 정리를 프레임워크 콜백에만 맡긴 것이다. Django Channels는 consumer가 예외로 끝나면 websocket_disconnect를 부르지 않는다. 다른 프레임워크도 비슷한 구멍이 있다. 정상 종료 경로에서만 정리가 돌고, 예외 경로에서는 빠진다.
consumer 진입점을 try/finally로 감싸 어떻게 끝나든 정리가 돌게 하고, 정상 종료 때 콜백과 겹쳐 두 번 하지 않게 표시를 둔다.
from channels.generic.websocket import AsyncWebsocketConsumer
class RoomConsumer(AsyncWebsocketConsumer):
_cleaned = False
async def __call__(self, scope, receive, send):
try:
await super().__call__(scope, receive, send)
finally:
await self.cleanup()
async def disconnect(self, code):
await self.cleanup()
async def cleanup(self):
if self._cleaned:
return
self._cleaned = True
room = getattr(self, "room", None)
if room:
await self.channel_layer.group_discard(room, self.channel_name)
이미 끊긴 상대에게 보내다 나는 예외(ClientDisconnected 등)는 삼키고 「닫는 중」으로 표시해, 정리 도중 또 다른 예외가 나지 않게 한다.