접속자 대기열 시스템 #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)
      • 여러가지 (108)
        • 알고리즘 & 자료구조 (73)
        • 오류 (4)
        • 이것저것 (29)
        • 일기 (1)
      • 프레임워크 (39)
        • 자바 스프링 (39)
        • React Native (0)
      • 프로그래밍 언어 (39)
        • 파이썬 (31)
        • 자바 (3)
        • 스프링부트 (5)
      • 컴퓨터 구조와 운영체제 (3)
      • DB (17)
        • SQL (0)
        • Redis (17)
      • 클라우드 컴퓨팅 (2)
        • 도커 (2)
        • AWS (0)
      • 스케쥴 (65)
        • 세미나 (0)
        • 수료 (0)
        • 스터디 (24)
        • 시험 (41)
      • 트러블슈팅 (1)
      • 자격증 (0)
        • 정보처리기사 (0)
      • 재태크 (0)
        • 암호화폐 (0)
        • 기타 (0)
  • 블로그 메뉴

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

  • 공지사항

  • 인기 글

  • 태그

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

  • 최근 글

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

티스토리툴바