diff --git a/backend/src/main/java/io/f1/backend/domain/game/app/GameService.java b/backend/src/main/java/io/f1/backend/domain/game/app/GameService.java index f4cf22e3..b401c078 100644 --- a/backend/src/main/java/io/f1/backend/domain/game/app/GameService.java +++ b/backend/src/main/java/io/f1/backend/domain/game/app/GameService.java @@ -59,7 +59,7 @@ public class GameService { private final RoomRepository roomRepository; private final ApplicationEventPublisher eventPublisher; - @DistributedLock(prefix = "room", key = "#roomId", waitTime = 0) + @DistributedLock(prefix = "room", key = "#roomId") public void gameStart(Long roomId, UserPrincipal principal) { String destination = getDestination(roomId); diff --git a/backend/src/main/java/io/f1/backend/domain/game/sse/app/SseService.java b/backend/src/main/java/io/f1/backend/domain/game/sse/app/SseService.java index 0873f5ff..cd9969c7 100644 --- a/backend/src/main/java/io/f1/backend/domain/game/sse/app/SseService.java +++ b/backend/src/main/java/io/f1/backend/domain/game/sse/app/SseService.java @@ -9,28 +9,30 @@ import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; import java.io.IOException; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; @Service @RequiredArgsConstructor public class SseService { private final SseEmitterRepository emitterRepository; + private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); public SseEmitter subscribe() { SseEmitter emitter = new SseEmitter(1_800_000L); emitterRepository.save(emitter); try { - // emitter 정상 전송확인 메시지 emitter.send(SseEmitter.event().name("connect").data("connected")); + startHeartBeat(emitter); } catch (IOException e) { - // emitter send() 호출 시 예외 처리 - emitterRepository.remove(emitter); + emitter.completeWithError(e); } return emitter; } - // 로비로 SSE 메시지를 쏘기위한 메서드 public void notifyLobbyUpdate(LobbySseEvent event) { for (SseEmitter emitter : emitterRepository.getAll()) { try { @@ -40,4 +42,18 @@ public void notifyLobbyUpdate(LobbySseEvent event) { } } } + + private void startHeartBeat(SseEmitter emitter) { + scheduler.scheduleAtFixedRate( + () -> { + try { + emitter.send(SseEmitter.event().name("heartbeat").data("sse-alive")); + } catch (IOException e) { + emitterRepository.remove(emitter); + } + }, + 5, + 60, + TimeUnit.SECONDS); + } } diff --git a/backend/src/main/java/io/f1/backend/domain/game/sse/store/SseEmitterRepository.java b/backend/src/main/java/io/f1/backend/domain/game/sse/store/SseEmitterRepository.java index c5236dec..fb50bf7e 100644 --- a/backend/src/main/java/io/f1/backend/domain/game/sse/store/SseEmitterRepository.java +++ b/backend/src/main/java/io/f1/backend/domain/game/sse/store/SseEmitterRepository.java @@ -13,18 +13,16 @@ public class SseEmitterRepository { public void save(SseEmitter emitter) { emitters.add(emitter); - // 연결종료 객체정리 + emitter.onCompletion(() -> emitters.remove(emitter)); emitter.onTimeout(() -> emitters.remove(emitter)); emitter.onError(error -> emitters.remove(emitter)); } - // 연결 종료 객체 정리 public void remove(SseEmitter emitter) { emitters.remove(emitter); } - // 브로드캐스팅 public List getAll() { return emitters; }