Skip to content

Commit aa744ce

Browse files
committed
♻️ 이전 방 튕기기 roomId 고정
1 parent 2138e67 commit aa744ce

File tree

6 files changed

+96
-53
lines changed

6 files changed

+96
-53
lines changed

backend/src/main/java/io/f1/backend/domain/game/app/RoomService.java

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ public class RoomService {
6767
private final UserRoomRepository userRoomRepository;
6868
private final AtomicLong roomIdGenerator = new AtomicLong(0);
6969
private final ApplicationEventPublisher eventPublisher;
70+
private final Map<String, Long> sessionRoomMap = new ConcurrentHashMap<>();
7071

7172
private final DisconnectTaskManager disconnectTasks;
7273
private final MessageSender messageSender;
@@ -375,6 +376,11 @@ public Room findRoom(Long roomId) {
375376
.orElseThrow(() -> new CustomException(RoomErrorCode.ROOM_NOT_FOUND));
376377
}
377378

379+
public boolean existsRoom(Long roomId) {
380+
return roomRepository
381+
.findRoom(roomId).isPresent();
382+
}
383+
378384
private void removeRoom(Room room) {
379385
Long roomId = room.getId();
380386
roomRepository.removeRoom(roomId);
@@ -397,8 +403,8 @@ private void changeHost(Room room, Player host) {
397403

398404
public void exitRoomForDisconnectedPlayer(Long roomId, Player player) {
399405
lockExecutor.executeWithLock(
400-
USER_LOCK_PREFIX,player.getId(),()->{
401-
lockExecutor.executeWithLock(ROOM_LOCK_PREFIX, roomId,()->{
406+
USER_LOCK_PREFIX, player.getId(), () -> {
407+
lockExecutor.executeWithLock(ROOM_LOCK_PREFIX, roomId, () -> {
402408
// 연결 끊긴 플레이어 exit 로직 타게 해주기
403409
Room room = findRoom(roomId);
404410

@@ -461,7 +467,24 @@ public boolean isUserInAnyRoom(Long userId) {
461467
return userRoomRepository.isUserInAnyRoom(userId);
462468
}
463469

464-
public Long getRoomIdByUserId(Long userId) {
470+
private Long getRoomIdByUserId(Long userId) {
471+
return userRoomRepository.getRoomId(userId);
472+
}
473+
474+
@DistributedLock(prefix = "user", key = "#userId")
475+
public Long getRoomIdByUserIdWithLock(Long userId) {
465476
return userRoomRepository.getRoomId(userId);
466477
}
478+
479+
public void addSessionRoomId(String sessionId, Long roomId) {
480+
sessionRoomMap.put(sessionId, roomId);
481+
}
482+
483+
public Long getRoomIdBySessionId(String sessionId) {
484+
return sessionRoomMap.get(sessionId);
485+
}
486+
487+
public void removeSessionRoomId(String sessionId) {
488+
sessionRoomMap.remove(sessionId);
489+
}
467490
}

backend/src/main/java/io/f1/backend/domain/game/websocket/HeartbeatMonitor.java

Lines changed: 46 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,26 @@
11
package io.f1.backend.domain.game.websocket;
22

3+
import static io.f1.backend.domain.game.app.RoomService.ROOM_LOCK_PREFIX;
4+
import static io.f1.backend.domain.game.app.RoomService.USER_LOCK_PREFIX;
35
import static io.f1.backend.domain.game.websocket.WebSocketUtils.getUserDestination;
46

57
import io.f1.backend.domain.game.app.RoomService;
68
import io.f1.backend.domain.game.dto.MessageType;
79
import io.f1.backend.domain.game.dto.response.HeartbeatResponse;
8-
10+
import io.f1.backend.domain.user.dto.UserPrincipal;
11+
import io.f1.backend.global.lock.LockExecutor;
12+
import java.security.Principal;
13+
import java.util.Map;
14+
import java.util.concurrent.ConcurrentHashMap;
915
import lombok.RequiredArgsConstructor;
1016
import lombok.extern.slf4j.Slf4j;
11-
1217
import org.springframework.messaging.simp.user.SimpSession;
1318
import org.springframework.messaging.simp.user.SimpUser;
1419
import org.springframework.messaging.simp.user.SimpUserRegistry;
1520
import org.springframework.scheduling.annotation.Scheduled;
21+
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
1622
import org.springframework.stereotype.Component;
1723

18-
import java.util.Map;
19-
import java.util.concurrent.ConcurrentHashMap;
20-
2124
@Slf4j
2225
@Component
2326
@RequiredArgsConstructor
@@ -32,6 +35,7 @@ public class HeartbeatMonitor {
3235
private final MessageSender messageSender;
3336
private final RoomService roomService;
3437
private final SimpUserRegistry simpUserRegistry;
38+
private final LockExecutor lockExecutor;
3539

3640
@Scheduled(fixedDelay = HEARTBEAT_CHECK_INTERVAL_MS)
3741
public void monitorClientHeartbeat() {
@@ -41,42 +45,50 @@ public void monitorClientHeartbeat() {
4145
}
4246

4347
simpUserRegistry
44-
.getUsers()
45-
.forEach(
46-
user ->
47-
user.getSessions()
48-
.forEach(session -> handleSessionHeartbeat(user, session)));
48+
.getUsers()
49+
.forEach(
50+
user ->
51+
user.getSessions()
52+
.forEach(session -> handleSessionHeartbeat(user, session)));
4953
}
5054

5155
private void handleSessionHeartbeat(SimpUser user, SimpSession session) {
5256
String sessionId = session.getId();
5357

5458
/* ping */
5559
messageSender.sendPersonal(
56-
getUserDestination(),
57-
MessageType.HEARTBEAT,
58-
new HeartbeatResponse(DIRECTION),
59-
user.getName());
60-
61-
// todo FE 개발 될때까지 주석 처리
62-
// missedPongCounter.merge(sessionId, 1, Integer::sum);
63-
// int missedCnt = missedPongCounter.get(sessionId);
64-
//
65-
// /* max_missed_heartbeats 이상 pong 이 안왔을때 - disconnect 처리 */
66-
// if (missedCnt >= MAX_MISSED_HEARTBEATS) {
67-
//
68-
// Principal principal = user.getPrincipal();
69-
//
70-
// if (principal instanceof UsernamePasswordAuthenticationToken token
71-
// && token.getPrincipal() instanceof UserPrincipal userPrincipal) {
72-
//
73-
// Long userId = userPrincipal.getUserId();
74-
// Long roomId = roomService.getRoomIdByUserId(userId);
75-
//
76-
// roomService.disconnectOrExitRoom(roomId, userPrincipal);
77-
// }
78-
// cleanSession(sessionId);
79-
// }
60+
getUserDestination(),
61+
MessageType.HEARTBEAT,
62+
new HeartbeatResponse(DIRECTION),
63+
user.getName());
64+
65+
missedPongCounter.merge(sessionId, 1, Integer::sum);
66+
int missedCnt = missedPongCounter.get(sessionId);
67+
68+
/* max_missed_heartbeats 이상 pong 이 안왔을때 - disconnect 처리 */
69+
log.info("missedCnt = {}", missedCnt);
70+
if (missedCnt >= MAX_MISSED_HEARTBEATS) {
71+
72+
Principal principal = user.getPrincipal();
73+
74+
if (principal instanceof UsernamePasswordAuthenticationToken token
75+
&& token.getPrincipal() instanceof UserPrincipal userPrincipal) {
76+
77+
Long userId = userPrincipal.getUserId();
78+
Long roomId = roomService.getRoomIdByUserIdWithLock(userId);
79+
80+
lockExecutor.executeWithLock(USER_LOCK_PREFIX, userId,
81+
() -> {
82+
lockExecutor.executeWithLock(ROOM_LOCK_PREFIX, roomId,
83+
() -> {
84+
roomService.disconnectOrExitRoom(roomId, userPrincipal);
85+
86+
});
87+
});
88+
89+
}
90+
cleanSession(sessionId);
91+
}
8092
}
8193

8294
public void resetMissedPongCount(String sessionId) {

backend/src/main/java/io/f1/backend/domain/game/websocket/controller/GameSocketController.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package io.f1.backend.domain.game.websocket.controller;
22

3+
import static io.f1.backend.domain.game.websocket.WebSocketUtils.getSessionId;
34
import static io.f1.backend.domain.game.websocket.WebSocketUtils.getSessionUser;
45

56
import io.f1.backend.domain.game.app.ChatService;
@@ -33,6 +34,7 @@ public void initializeRoomSocket(@DestinationVariable Long roomId, Message<?> me
3334
UserPrincipal principal = getSessionUser(message);
3435

3536
roomService.initializeRoomSocket(roomId, principal);
37+
roomService.addSessionRoomId(getSessionId(message), roomId);
3638
}
3739

3840
@MessageMapping("/room/reconnect/{roomId}")
@@ -41,6 +43,7 @@ public void reconnect(@DestinationVariable Long roomId, Message<?> message) {
4143
UserPrincipal principal = getSessionUser(message);
4244
roomService.changeConnectedStatusWithLock(roomId, principal.getUserId(), ConnectionState.CONNECTED);
4345
roomService.reconnectSendResponseWithLock(roomId, principal);
46+
roomService.addSessionRoomId(getSessionId(message), roomId);
4447
}
4548

4649
@MessageMapping("/room/exit/{roomId}")

backend/src/main/java/io/f1/backend/domain/game/websocket/controller/HeartbeatController.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ public class HeartbeatController {
2020
public void handlePong(Message<?> message) {
2121
String sessionId = getSessionId(message);
2222

23-
// todo FE 개발 될때까지 주석 처리
24-
// heartbeatMonitor.resetMissedPongCount(sessionId);
23+
heartbeatMonitor.resetMissedPongCount(sessionId);
2524
}
2625
}

backend/src/main/java/io/f1/backend/domain/game/websocket/eventlistener/WebsocketEventListener.java

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -36,17 +36,21 @@ public void handleDisconnectedListener(SessionDisconnectEvent event) {
3636
UserPrincipal principal = getSessionUser(message);
3737

3838
Long userId = principal.getUserId();
39+
String sessionId = event.getSessionId();
3940

40-
// todo FE 개발 될때까지 주석 처리
41-
// heartbeatMonitor.cleanSession(event.getSessionId());
41+
heartbeatMonitor.cleanSession(sessionId);
42+
Long roomId = roomService.getRoomIdBySessionId(sessionId);
43+
roomService.removeSessionRoomId(sessionId);
4244

4345
/* 정상 로직 */
4446
if (!roomService.isUserInAnyRoom(userId)) {
4547
return;
4648
}
4749

48-
Long roomId = lockExecutor.executeWithLock(USER_LOCK_PREFIX, userId,
49-
() -> roomService.getRoomIdByUserId(userId));
50+
if(!roomService.existsRoom(roomId)) {
51+
return;
52+
}
53+
5054

5155
lockExecutor.executeWithLock(
5256
ROOM_LOCK_PREFIX, roomId, () -> roomService.changeConnectedStatus(roomId, userId,
@@ -55,14 +59,16 @@ public void handleDisconnectedListener(SessionDisconnectEvent event) {
5559
taskManager.scheduleDisconnectTask(
5660
userId,
5761
() -> {
58-
lockExecutor.executeWithLock(ROOM_LOCK_PREFIX, roomId,
62+
lockExecutor.executeWithLock(USER_LOCK_PREFIX, userId,
5963
() -> {
60-
if (ConnectionState.DISCONNECTED.equals(
61-
roomService.getPlayerState(userId, roomId))) {
62-
roomService.disconnectOrExitRoom(roomId, principal);
63-
}
64-
}
65-
);
64+
lockExecutor.executeWithLock(ROOM_LOCK_PREFIX, roomId,
65+
() -> {
66+
if (ConnectionState.DISCONNECTED.equals(
67+
roomService.getPlayerState(userId, roomId))) {
68+
roomService.disconnectOrExitRoom(roomId, principal);
69+
}
70+
});
71+
});
6672
});
6773
}
6874
}

backend/src/main/java/io/f1/backend/global/lock/LockExecutor.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,10 @@ public <T> T executeWithLock(String prefix, Object key, Supplier<T> supplier) {
3737
acquired = rlock.tryLock(DEFAULT_WAIT_TIME, DEFAULT_LEASE_TIME, DEFAULT_TIME_UNIT);
3838

3939
if(!acquired) {
40-
log.warn("[DistributedLock] Lock acquisition failed: {}", key);
40+
log.warn("[LockExecutor] Lock acquisition failed: {}", key);
4141
throw new CustomException(CommonErrorCode.LOCK_ACQUISITION_FAILED);
4242
}
43-
log.info("[DistributedLock] Lock acquired: {}", key);
43+
log.info("[LockExecutor] Lock acquired: {}", key);
4444

4545
return supplier.get();
4646
} catch (InterruptedException e) {
@@ -49,7 +49,7 @@ public <T> T executeWithLock(String prefix, Object key, Supplier<T> supplier) {
4949
} finally {
5050
if (acquired && rlock.isHeldByCurrentThread()) {
5151
rlock.unlock();
52-
log.info("[DistributedLock] Lock released: {}", key);
52+
log.info("[LockExecutor] Lock released: {}", key);
5353
}
5454
}
5555
}

0 commit comments

Comments
 (0)