STOMP와 Scheduler 현재 접속자 구현하기

박병주·2025년 2월 16일
0

회고

목록 보기
2/2

웹 내에서 방에서 게임에 초대하기 위한 현재 접속자를 구현하게 되었다. (구현해본적 없는 기능이니... 정답에 멀어진 방법일 수 있다.)

문제점은 웹 브라우저 종료 방식에는 여러 방법이 있다는 것. 즉 접속자 데이터를 추가하는 것에는 제한이 없지만, 접속이 끝난 사용자에 대한 처리는 애매하다는 것...

아이디어 구상

구현에 필요한 생각.

  1. 현 프로젝트에서 가진 사용 가능한 자원은?
  2. 접속자를 어떻게 체크하지?
  3. 접속여부를 어떻게 제공하지?

구체화!

사용가능한 자원은?

프로젝트가 복잡해지지 않으려면 최대한 지금 가지고 있는, 이미 종속성 추가된 기능 내에서 개발해야 한다. 현재 접속여부 구현에 필요한 자원은 Redis, Scheduler, STOMP 정도로 생각했다.

아이디어

접속자가 "나 접속해 있어요" 서버에 알려주면 되는 것 이라는 생각에 그럼 서버에서 접속 request를 보내주는 유저에 대해서 정보를 관리하면 되겠다 싶었다.

구상한 접속자 확인 프로세스는 위 사진과 같다. 스케쥴러를 통해서 소켓 구독자들에게 n초에 한 번 접속여부를 묻는다. 답변이 돌아오면 해당 유저의 번호를 Redis에서 관리 시킨다.
Redis에서는 Set을 이용해서 유저의 번호를 관리하고 매 일정 주기마다 Set을 초기화해서 실시간 데이터로 데이터를 제공할 수 있도록 한다.

트러블

이렇게 구현 했을 때 생각지 못한 트러블에 마주쳤다. 로컬에서는 거의 즉각적인 응답으로 확인 하지 못했었다. 배포 서버에서 점검 해보니 클라이언트들의 응답 속도는 각자 달랐고, Set이 초기화 된 직후 아직 접속 요청을 받지 못한 시점에서 접속자 여부 데이터를 제공하면 신뢰성 없는 데이터가 나가게 되었다.

슈팅!

두 개의 셋으로 접속자를 관리해서 응답 시간차를 극복해 보자!

구현 코드

  1. 일정 시간마다 접속자를 초기화 해주는 스케쥴러
    • 20초마다 Set을 교체 및 초기화 해준다
    • Redis 내에서 백업으로 activeUsers를 교체하고 백업 Set을 초기화 한다.
@Scheduled(fixedRate = 20000)
    public void userActiveManage() {
        ValueOperations<String, Object> ops = redisTemplate.opsForValue();

        // 1. 접속자 자료구조 2개 체킹
        Set<Long> activeBackupUsers = ops.get("activeBackupUsers") != null ? (Set<Long>) ops.get("activeBackupUsers") : new HashSet<>();
        Set<Long> activeUsersTemp = new HashSet<>();

        ops.set("activeUsers", activeBackupUsers);
        ops.set("activeBackupUsers", activeUsersTemp);

    }
  1. n초 마다 신호를 보내주는 스케쥴러
    @Scheduled(fixedRate = 3000) // 3초 마다
    public void sendActiveDataSchedule() {
        messagingTemplate.convertAndSend("/active", "ping");
    }
  1. 요청 들어온 유저를 redis에 추가하는 메소드
    • 백업과 유저 둘 다 유저를 추가해서 관리하는 것이 아이디어의 포인트
public void addActiveUser(Long memeberId) {
        ValueOperations<String, Object> ops = redisTemplate.opsForValue();
        Set<Long> activeUsers = (Set<Long>) ops.get("activeUsers");
        Set<Long> activeBackupUsers = (Set<Long>) ops.get("activeBackupUsers");
        activeUsers.add(memeberId);
        activeBackupUsers.add(memeberId);
        System.out.println("접속중 : " + activeUsers);
        ops.set("activeUsers", activeUsers);
        ops.set("activeBackupUsers", activeBackupUsers);
    }

결론

이제 접속자 정보를 제공할때 contains를 통해 boolean으로 값을 조회 할 수 있게 되었다!

profile
응애

0개의 댓글