접속자 대기열 시스템 #7- 대기열 스케줄러 개발

2024. 10. 10. 17:13·프레임워크/자바 스프링

접속자 대기열 시스템 #7 - 대기열 스케줄러 개발

이번 단계에서는 대기열에서 사용자를 자동으로 허용하는 스케줄러를 구현합니다. 이 스케줄러는 주기적으로 대기열을 확인하고, 일정 수의 사용자를 허용 목록으로 이동시키는 역할을 합니다. 이를 통해 대기열 시스템을 자동화하고 효율성을 높일 수 있습니다.

1. UserQueueService 클래스

UserQueueService 클래스는 대기열과 관련된 비즈니스 로직을 담당하며, 스케줄러를 통해 대기열 관리 작업을 수행합니다.

스케줄러 메서드: scheduleAllowUser

@Slf4j
@Service
@RequiredArgsConstructor
public class UserQueueService {

    private final ReactiveRedisTemplate<String, String> reactiveRedisTemplate;
    private final String USER_QUEUE_WAIT_KEY = "users:queue:%s:wait";
    private final String USER_QUEUE_WAIT_KEY_FOR_SCAN = "users:queue:*:wait";
    private final String USER_QUEUE_PROCEED_KEY = "users:queue:%s:proceed";

    @Value("${scheduler.enabled}")
    private Boolean scheduling = false;

    @Scheduled(initialDelay = 5000, fixedDelay = 10000)
    public void scheduleAllowUser() {
        if (!scheduling) {
            log.info("passed scheduling...");
            return;
        }
        log.info("called scheduling...");

        var maxAllowUserCount = 3L;

        reactiveRedisTemplate.scan(ScanOptions.scanOptions()
                    .match(USER_QUEUE_WAIT_KEY_FOR_SCAN)
                    .count(100)
                    .build())
                .map(key -> key.split(":")[2])
                .flatMap(queue -> allowUser(queue, maxAllowUserCount)
                        .map(allowed -> Tuples.of(queue, allowed)))
                .doOnNext(tuple -> log.info("Tried %d and allowed %d member of %s queue"
                        .formatted(maxAllowUserCount, tuple.getT2(), tuple.getT1())))
                .subscribe();
    }
}

1. 기능 설명:

  • @Scheduled 어노테이션을 사용하여 스케줄러를 설정합니다. initialDelay = 5000은 서버 시작 후 5초 후에 첫 실행을 의미하며, fixedDelay = 10000은 이후 10초마다 실행됩니다.
  • scheduleAllowUser 메서드는 Redis를 통해 대기열을 검색하고, 각 대기열에서 지정된 수의 사용자를 허용합니다.

2. 스케줄러 활성화 조건:

  • scheduling 변수가 true일 때만 스케줄러가 동작합니다. 이는 application.yml 파일에서 설정됩니다.

3. 대기열 스캔과 사용자 허용:

  • reactiveRedisTemplate.scan(...)을 통해 Redis에서 users:queue:*:wait 형식의 모든 키를 검색하여 각 대기열을 찾습니다.
  • .flatMap(queue -> allowUser(...)): 각 대기열에서 maxAllowUserCount 수만큼 사용자를 허용합니다.
  • .doOnNext: 허용된 사용자 수와 대기열 이름을 로그에 출력합니다.

스케줄러의 코드 분석

reactiveRedisTemplate.scan(ScanOptions.scanOptions()
                .match(USER_QUEUE_WAIT_KEY_FOR_SCAN)
                .count(100)
                .build())
        .map(key -> key.split(":")[2])
        .flatMap(queue -> allowUser(queue, maxAllowUserCount)
                .map(allowed -> Tuples.of(queue, allowed)))
        .doOnNext(tuple -> log.info("Tried %d and allowed %d member of %s queue"
                .formatted(maxAllowUserCount, tuple.getT2(), tuple.getT1())))
        .subscribe();
  1. 대기열 스캔:
    • reactiveRedisTemplate.scan(...)은 Redis의 SCAN 명령어를 사용해 비동기적으로 키를 검색합니다. 여기서 ScanOptions는 검색 조건을 지정합니다.
    • .match(USER_QUEUE_WAIT_KEY_FOR_SCAN)은 "users:queue:*:wait" 형식의 키를 찾도록 설정하고, .count(100)은 한 번에 최대 100개의 키를 반환합니다.
  2. 키 가공:
    • .map(key -> key.split(":")[2])는 검색된 키를 대기열 이름으로 가공합니다. "users:queue:<queue_name>:wait" 형식의 키에서 <queue_name>을 추출하는 작업입니다.
  3. 사용자 허용:
    • .flatMap(queue -> allowUser(queue, maxAllowUserCount))를 통해 각 대기열에서 최대 maxAllowUserCount 수만큼 사용자를 허용합니다.
    • .map(allowed -> Tuples.of(queue, allowed))는 허용된 사용자 수와 대기열 이름을 튜플 형식으로 변환합니다.
  4. 로그 출력:
    • .doOnNext(tuple -> log.info(...))를 통해 각 대기열에서 허용된 사용자 수와 대기열 이름을 로그로 남깁니다.
  5. 스트림 실행:
    • .subscribe() 메서드가 호출되면 스트림이 실행되고, 모든 작업이 비동기적으로 처리됩니다.

2. WaitingApiApplication 클래스

WaitingApiApplication 클래스는 애플리케이션의 진입점이며, 스케줄러 기능을 활성화합니다.

package com.example.flow;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

@EnableScheduling
@SpringBootApplication
public class WaitingApiApplication {

    public static void main(String[] args) {
        SpringApplication.run(WaitingApiApplication.class, args);
    }

}

1. @EnableScheduling 어노테이션:

  • 이 어노테이션은 애플리케이션에서 스케줄링 기능을 활성화합니다. 이를 통해 @Scheduled로 설정한 메서드가 주기적으로 실행될 수 있습니다.

3. application.yml 설정

스케줄러의 활성화 여부를 설정하는 application.yml 파일입니다.

scheduler:
  enabled: true

---
spring:
  config:
    activate:
      on-profile: test

scheduler:
  enabled: false

1. 프로덕션 환경 설정:

  • 기본적으로 scheduler.enabled 값이 true로 설정되어, 스케줄러가 활성화됩니다.

2. 테스트 환경 설정:

  • spring.config.activate.on-profile: test 조건이 활성화되면, scheduler.enabled 값이 false로 설정되어 스케줄러가 비활성화됩니다.

QnA

Q1. @Scheduled 어노테이션을 사용할 때 주의해야 할 점은 무엇인가요?

  • @Scheduled 메서드는 기본적으로 하나의 스레드에서 실행되므로, 오랜 시간 동안 실행되는 작업이 있을 경우 동시성 문제가 발생할 수 있습니다. 필요하다면, @Async 어노테이션을 함께 사용하여 비동기적으로 실행할 수 있습니다.

Q2. 스케줄러의 실행 간격을 조정하려면 어떻게 해야 하나요?

  • @Scheduled 어노테이션의 initialDelay와 fixedDelay 값을 변경하여 조정할 수 있습니다. fixedDelay는 이전 작업이 완료된 후부터 다음 작업이 실행되기까지의 간격을 나타냅니다.

Q3. 스케줄러가 주기적으로 허용할 사용자 수를 동적으로 조정할 수 있나요?

  • 네, 스케줄러 메서드에서 maxAllowUserCount 값을 외부 설정 파일이나 데이터베이스에서 동적으로 읽어와 설정할 수 있습니다.

Q4. 여러 인스턴스가 실행 중일 때 스케줄러의 동작은 어떻게 관리하나요?

  • 여러 인스턴스에서 동일한 스케줄러가 실행되면, 중복 작업이 발생할 수 있습니다. 이를 방지하기 위해 분산 락을 사용하여 한 번에 하나의 인스턴스만 스케줄러 작업을 수행하도록 설정할 수 있습니다.

Q5. 스케줄러가 허용할 사용자의 수를 초과한 경우에는 어떻게 되나요?

  • allowUser 메서드는 실제 대기열에 있는 사용자 수보다 허용할 수 있는 사용자 수가 적다면, 허용 가능한 최대 사용자 수만큼만 처리됩니다. 예를 들어, 대기열에 2명만 있고 최대 허용 수가 5명일 때, 실제로는 2명만 허용됩니다.

Q6. 스케줄러를 특정 시간대에만 활성화할 수 있나요?

  • @Scheduled 어노테이션 대신 Cron 표현식을 사용하여 특정 시간대에 스케줄러를 실행할 수 있습니다. 예를 들어, "0 0 9 * * *" 표현식은 매일 오전 9시에 스케줄러를 실행합니다.

Q7. 스케줄러가 비활성화된 경우에도 수동으로 실행할 수 있나요?

  • 스케줄러가 비활성화된 경우, 수동으로 scheduleAllowUser 메서드를 호출하여 작업을 수행할 수 있습니다. 이를 위해 별도의 API 엔드포인트를
  • 만들거나, 애플리케이션 내부에서 직접 호출할 수 있습니다.

Q8. scan 메서드로 검색된 대기열이 많을 경우 성능 문제는 없나요?

  • scan 메서드는 Redis의 SCAN 명령어를 사용하며, 이는 대량의 키를 효율적으로 검색하는 방식입니다. 한 번에 검색할 키의 수를 조절하여 성능을 최적화할 수 있습니다.

Q9. 테스트 환경에서 스케줄러를 비활성화하는 이유는 무엇인가요?

  • 테스트 환경에서는 특정 시나리오를 재현하거나, 데이터를 조작하여 테스트하기 위해 스케줄러를 비활성화하는 것이 일반적입니다. 이를 통해 테스트 중 발생할 수 있는 불필요한 작업을 방지할 수 있습니다.

Q10. 스케줄러가 실행 중일 때 Redis 서버가 중단되면 어떻게 되나요?

  • Redis 서버가 중단되면, ReactiveRedisTemplate을 통해 수행되는 작업이 실패하게 됩니다. 이를 대비해 에러를 적절히 처리하고, 스케줄러가 중단되지 않도록 하는 것이 중요합니다.

위와 같이, 스케줄러를 사용한 대기열 관리 시스템을 개발하면서 고려해야 할 사항들과 동작 방식을 이해할 수 있습니다. 이를 통해 대기열 시스템의 자동화와 효율성을 높일 수 있습니다.

저작자표시 (새창열림)

'프레임워크 > 자바 스프링' 카테고리의 다른 글

접속자 대기열 시스템 #9- 테스트  (0) 2024.10.10
접속자 대기열 시스템 #8- 대기열 이탈  (0) 2024.10.10
접속자 대기열 시스템 #6- 접속 대기 웹페이지 개발  (1) 2024.10.10
접속자 대기열 시스템 #5- Redis를 이용한 대기열 관리 및 웹페이지 진입 API 구현  (1) 2024.10.10
접속자 대기열 시스템 #3- 셋업  (2) 2024.10.10
'프레임워크/자바 스프링' 카테고리의 다른 글
  • 접속자 대기열 시스템 #9- 테스트
  • 접속자 대기열 시스템 #8- 대기열 이탈
  • 접속자 대기열 시스템 #6- 접속 대기 웹페이지 개발
  • 접속자 대기열 시스템 #5- Redis를 이용한 대기열 관리 및 웹페이지 진입 API 구현
hyeseong-dev
hyeseong-dev
안녕하세요. 백엔드 개발자 이혜성입니다.
  • hyeseong-dev
    어제 오늘 그리고 내일
    hyeseong-dev
  • 전체
    오늘
    어제
    • 분류 전체보기 (283)
      • 여러가지 (107)
        • 알고리즘 & 자료구조 (72)
        • 오류 (4)
        • 이것저것 (29)
        • 일기 (1)
      • 프레임워크 (39)
        • 자바 스프링 (39)
        • React Native (0)
      • 프로그래밍 언어 (38)
        • 파이썬 (30)
        • 자바 (3)
        • 스프링부트 (5)
      • 운영체제 (0)
      • DB (17)
        • SQL (0)
        • Redis (17)
      • 클라우드 컴퓨팅 (2)
        • 도커 (2)
        • AWS (0)
      • 스케쥴 (65)
        • 세미나 (0)
        • 수료 (0)
        • 스터디 (24)
        • 시험 (41)
      • 트러블슈팅 (1)
      • 자격증 (0)
        • 정보처리기사 (0)
      • 재태크 (5)
        • 암호화폐 (5)
        • 기타 (0)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    ecs
    WebFlux
    reactor
    자바
    OOP
    항해99
    #개발자포트폴리오 #개발자이력서 #개발자취업 #개발자취준 #코딩테스트 #항해99 #취리코 #취업리부트코스
    FastAPI
    취업리부트
    그리디
    mybatis
    docker
    Python
    완전탐색
    Docker-compose
    RDS
    백준
    spring
    Redis
    AWS
    Spring Boot
    DP
    celery
    SAA
    파이썬
    EC2
    프로그래머스
    java
    Spring WebFlux
    시험
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.0
hyeseong-dev
접속자 대기열 시스템 #7- 대기열 스케줄러 개발
상단으로

티스토리툴바