본문 바로가기
기술 트렌드 & 새로운 학습

Rate Limiting

by DoRightting 2024. 11. 13.

1. 구현된 Rate Limiting 시스템

1.1 SimpleRateLimiter 구현

public class SimpleRateLimiter {
    private final int maxRequests;
    private final long timeWindowMillis;
    private final AtomicInteger currentRequests;
    private final AtomicLong windowStartTime;

    public SimpleRateLimiter(int maxRequests, Duration timeWindow) {
        this.maxRequests = maxRequests;
        this.timeWindowMillis = timeWindow.toMillis();
        this.currentRequests = new AtomicInteger(0);
        this.windowStartTime = new AtomicLong(System.currentTimeMillis());
    }

    public boolean tryAcquire() {
        long now = System.currentTimeMillis();
        long windowStart = windowStartTime.get();

        if (now - windowStart >= timeWindowMillis) {
            // 새로운 시간 윈도우 시작
            windowStartTime.set(now);
            currentRequests.set(1);
            return true;
        }

        return currentRequests.incrementAndGet() <= maxRequests;
    }

    public void waitForPermit() throws InterruptedException {
        while (!tryAcquire()) {
            Thread.sleep(100); // 100ms 대기 후 재시도
        }
    }
}

1.2 Rate Limiting 인터셉터

@Slf4j
public class RateLimitingInterceptor implements ClientHttpRequestInterceptor {
    private final SimpleRateLimiter rateLimiter;

    public RateLimitingInterceptor(double requestsPerMinute) {
        this.rateLimiter = new SimpleRateLimiter((int)requestsPerMinute, Duration.ofMinutes(1));
    }

    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body,
                                      ClientHttpRequestExecution execution) throws IOException {
        try {
            rateLimiter.waitForPermit();

            long startTime = System.currentTimeMillis();
            ClientHttpResponse response = execution.execute(request, body);
            long duration = System.currentTimeMillis() - startTime;

            log.debug("OpenAI API call completed in {}ms", duration);
            return response;

        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new IOException("Rate limiting interrupted", e);
        }
    }
}

1.3 OpenAI API 통합에서의 사용

@Configuration
public class OpenAiConfig {
    @Value("${openai.api.rate-limit.requests-per-minute}")
    private double requestsPerMinute;

    @Bean
    public OpenAiApi openAiApi(@Qualifier("openAiRestTemplate") RestTemplate openaiRestTemplate) {
        return OpenAiApi.builder()
                .requestsPerMinute(requestsPerMinute)
                .build();
    }
}

프로젝트에서 Rate Limiting을 구현한 이유와 방식

  1. 비용 관리
    • API 호출 횟수 제한으로 비용 관리
    • 예측 가능한 리소스 사용
@Value("${openai.api.rate-limit.requests-per-minute}") private double requestsPerMinute; // 설정 가능한 제한값

 

  1. 구현 방식
    • 슬라이딩 윈도우 방식 사용
    • 원자적 연산으로 동시성 보장
    • 설정 가능한 시간 윈도우와 요청 제한
public boolean tryAcquire() {
long now = System.currentTimeMillis(); 
    if (now - windowStart >= timeWindowMillis) {
    	windowStartTime.set(now); 
        currentRequests.set(1); 
        return true; 
        } 
    return currentRequests.incrementAndGet() <= maxRequests; 
}

 

Rate Limiting에서 발생할 수 있는 동시성 이슈 해결 방법

  1. 원자적 연산 사용
    • AtomicInteger, AtomicLong 사용
    • 경쟁 상태 방지
private final AtomicInteger currentRequests; 
private final AtomicLong windowStartTime; 
return currentRequests.incrementAndGet() <= maxRequests;

 

 

  2. 스레드 안전한 설계

  • 동시 요청 처리 보장
  • 인터럽트 처리 구현
public void waitForPermit() throws InterruptedException {
	while (!tryAcquire()) {
		Thread.sleep(100); 
		} 
}

 

 

Rate Limit 초과 시 대응

  1. 대기 및 재시도
    • 적절한 대기 시간 설정
    • 점진적 재시도
public void waitForPermit() throws InterruptedException {
	while (!tryAcquire()) {
    	Thread.sleep(100); // 100ms 대기 
        } 
}

 

  2. 예외 처리

  • 명확한 예외 전달
  • 클라이언트에 상태 전달

 

  • Rate Limiting 구현의 한계점과 개선 가능성
  1. 현재 한계
    • 메모리 내 카운터 사용
    • 서버 재시작시 카운터 초기화
    • 분산 환경 미고려
private final AtomicInteger currentRequests;

 

'기술 트렌드 & 새로운 학습' 카테고리의 다른 글

코사인 유사도 검색  (1) 2024.11.18
IVFFlat  (0) 2024.11.17
CompletableFuture  (0) 2024.11.14