개발 알다가도 모르겠네요

[Spring] Concurrency/Rate Limiter를 적용하여 API 요청을 제어해보자 본문

웹/Spring

[Spring] Concurrency/Rate Limiter를 적용하여 API 요청을 제어해보자

이재빵 2023. 8. 25. 08:45
728x90

최근 프로젝트에서 API 요청 수를 제어하기 위해 Rate Limiter를 적용하는 작업을 수행했다.

이 과정에서 겪은 문제점과 해결하기 위해 학습한 내용을 정리해보고자 한다.

 

* 업무 환경은 Laravel 프레임워크를 쓰고 있기 때문에, 지금 설명하려는 Spring에서의 Rate Limiter 구현과는 적용 알고리즘 정도의 차이가 있다.

Problem

1. 사용자 계정별 글 작성 수 제한 정책 추가

사용자마다 시간 당 글 작성 수를 제한해야 하는 기획팀의 요구사항이 있었다. (아래처럼 자동화 공격이나 도배성 뻘글 방지 목적도 있었다.)

 

2. AI 이미지 생성 API 다중 호출 시 서버 과부하로 인한 타임아웃 이슈

현재 운용 중인 단일 AI서버에서 이미지를 하나 생성하는데는 약 5~6초가 걸린다. 다수의 AI 이미지 생성 요청이 들어올 경우, 응답 지연으로 인해 커넥션 타임아웃되는 문제가 발생했다.

 

 

Solution

위의 문제들을 모두 해결할 수 있는 방법을 찾던 중, '가상 면접 사례로 배우는 대규모 시스템 설계 기초' 책에서 'Rate Limiter' 라는 것을 알게 되었다.

결론부터 말하자면, 애플리케이션 단에서 Redis + Rate Limit을 적용하여 1번 문제를 해결했다.

 

2번 문제는 Concurrency Limit 방식으로 해결했다. 현재 실행 중인 요청 수를 실시간으로 관리함으로써, 특정 시점에 동시에 처리할 수 있는 최대 요청 수를 제한하도록 하는 것이다. 요청이 들어오면 Redis에 카운트를 증가시키고, 응답을 보내면 카운트를 감소시키는 방식으로 구현 자체는 상당히 쉬웠다.

 

Rate Limit 이란?

API 또는 웹 서비스에서 특정 시간 동안 허용되는 최대 요청 횟수를 제한하는 기능

일반적으로 서비스 제공자가 서비스의 과부하를 방지하고, 무제한적인 요청을 방지하기 위해 사용된다.

 

 

제한 조건을 정해야한다.

유저의 요청 수가 임계치를 넘었을때 제한 하는 방식은 크게 3가지로 나눠볼 수 있다.

일단 공통적으로는 유저의 요청수가 초과하면 HTTP Status 429 - Too many requests 를 리턴하게 된다.

 

1. Hard Throttling : rate limit을 엄격하게 관리 (1개라도 넘으면 안됨)

 

2. Soft Throttling : 설정 %까지는 rate limit을 넘기는 것을 허용

 

3. Elastic or Dynamic Throttling : 서버 자원에 여유가 있을 때는 rate limit을 넘기더라도 요청을 허용

 

우리 팀은 보수적인 정책으로 1번을 채택했다.

 

기능 / 운영 측면에서의 조건

Rate limit을 구현할 때 요구 조건은 크게 구현과 운영의 측면으로 나누어 고려할 수 있다.

 

기능 측면

1. 클라이언트가 서버에 요청할 수 있는 횟수를 특정 임계치까지만 허용한다. (예: 1분당 40회)

2. 클라이언트의 요청횟수가 정책적으로 정해둔 임계치를 넘어설 경우에는 에러를 리턴해야 한다.

 

운영 측면

1. Rate Limit이 동작하는 서버는 안정적으로 동작해야 한다.

2. Rate Limit서버로 인해 서버의 처리량이 과도하게 늘어나거나 응답시간이 지연되지 않고 빠르게 Rate Limit을 처리할 수 있어야 한다.

 

 

Rate Limit 알고리즘

1. Token Bucket

- 버켓 안에 토큰을 담아두고, 요청마다 토큰을 소비하여 버켓 안의 토큰이 모두 소진되면 요청 제한

- 다음 refill 시간까지 요청 불가

*메모리는 효율적이지만, 분산 환경에서 동시에 들어오는 요청에 대해 Race condition 발생 가능성 있음.

 

2.  Leaky Bucket

- FIFO 형식의 Bucket Queue에 요청을 순서대로 담아 크기가 다 차면 요청 제한

- 다음 refill 시간이 되면 가장 먼저 들어온 요청 제거 후 신규 요청을 담음 (다 찬 상태에서 신규 요청은 버려짐)

 

3. Fixed Window Counter

- 고정된 크기 내에 일정 요청 개수만을 허용 (Twitter API에 rate limits 기능이 있다고 함)

- 특정 API를 1분 동안 5개의 요청만 받는다고 설정하면, 해당 시간동안 5개의 요청을 받은 뒤 접근 제한 처리

* 특정 간격 사이에 부하가 올 가능성 있음
(11:00:58 에 5개의 요청 & 11:01:02 에 5개의 요청 들어올 경우 4초 동안 10개의 요청이 들어오는 편향 케이스 발생)

 

 

4. Sliding Logs

- 모든 요청을 타임스탬프로 기록하고 현재 시점을 기준으로 시간 범위 내의 요청 개수에 따라 요청 제한

- 1분에 5개의 요청을 받는다고 할때, 현재 시간 ~ 1분 전의 요청 확인 (Log: Key-요청자, Value-타임스탬프)

- 즉, 1분 사이에 2개의 요청만 인입돼 있으면 3개의 요청 추가 가능

* 유저 각각의 타임스탬프를 모두 로깅하기 때문에 메모리 비효율적

 

 

5. Sliding Window Counter

- Fixed Window + Sliding Log. 타임스탬프를 모두 저장하지는 않고 카운터로 요청 개수에 따라 요청 제한

- Fixed Window의 경계점 부하 문제를 Sliding Log와 함께 범위를 조금씩 '움직이는 시간' 단위로 측정.

* 요청자 별 모든 타임스탬프 대신 카운트만 저장하기 때문에 메모리 효율성 개선

 

예를 들어, 1분에 10개의 요청을 받는다고 가정해보자.

11:00:00 ~ 11:01:00 사이에 9건, 11:01:00 ~ 11:02:00 사이에 5건의 요청이 들어왔다.

이때, 11:01:15 에 새로운 요청이 인입된다고 하면 시간 범위로 따졌을 때 25% 지점에 위치하게 되고, 그 전 11:00:00 ~ 11:01:00의 시간 범위는 75%의 비중을 차지하게 된다.

계산하게 되면 9 X 0.75 + 5 = 14.75 가 되고, 10건의 제한을 넘었기 때문에 11:01:15 의 요청은 거부된다.

다른 케이스로, 11:01:30 에 새로운 요청이 인입된다고 하면 시간 범위로 50% 지점에 위치하고, 그 전 11:00:00 ~ 11:01:00의 비중은 50%를 차지하게 된다.

9 X 0.5 + 5 = 9.5 가 되므로 11:01:30 의 요청은 승인된다.

 

 

 

일시적으로 많은 트래픽이 와도 토큰이 있다면 처리가 가능하면서 토큰 손실 처리를 통해 평균 처리 속도를 제한할 수 있다. 즉, 평균 유입 속도를 제한하고 처리 패킷 손실없이 특정 수준의 버스트 요청 허용할 수 있다.

 

이제 처음 문제점에 어떤 알고리즘을 적용할지 선택해 보자.

 

[사용자 계정별 글 작성 수 제한 정책 추가 문제]

토큰 버킷 알고리즘을 적용하여 일정한 속도로 토큰을 추가하고, 사용자가 글을 작성할 때 토큰을 소모하게 한다. 이를 통해 사용자 계정별로 글 작성 수를 제어할 수 있다.
글 작성 자체는 burst 트래픽(일시적인 트래픽 폭주)가 없었기 때문에, 허용하면서도 일정 기간 동안의 최대 요청 수를 제한하는 데 효과적이라고 판단했다.

 

*Laravel에서 지원하는 RateLimiter 기능 (ThrottleRequests) 은 Fixed Window Counter 알고리즘 기반.

 

Race Condition에 대응하자

그런데 아까 보면 토큰 버킷 알고리즘을 사용할 경우 Race Condition에 빠질 수 있다고 했다.

  • 레디스에서 카운터의 값을 읽음 (counter)
  • counter +1의 값이 임계치를 넘는지 확인
  • 넘지 않는다면 레디스에 보관된 카운터 값 +1

 

 

Concurrency Limit 케이스이긴 하지만, 2번 문제에서 요청 수를 초과했지만 그대로 AI이미지 생성 API에 인입되는 것을 확인할 수 있었다.

 

경쟁 조건 문제를 해결하는 가장 널리 알려진 해결책은 Lock이지만, 싱글스레드로 작동하게 될 수 있어 시스템의 성능을 떨어뜨릴 수 있다.

 

해결책은 두가지가 있는데 그중에서 루아 스크립트를 적용해보자.

  • 루아 스크립트(Lua script)
  • 레디스 자료구조의 정렬집합(sorted set)

루아 스크립트를 쓰면 어떤 일이 일어날까?

루아 스크립트가 실행되는 동안, 레디스는 blocked 상태가 된다고 한다. 즉 레디스는 루아 스크립트가 Atomic하게 실행되는 걸 보장하기 때문에, 분산 서버 환경 등에서 용이하게 쓰인다고 한다.

 

*루아 스크립트는 '원자적' 처리 목적이기 때문에, 동시성 제어를 우선시하는 분산 락과는 차이가 있다.

  • 원자성 처리: 트랜잭션 내의 모든 연산이 전부 수행되거나 전혀 수행되지 않도록 보장.
  • 동시성 제어: 여러 트랜잭션이 동시에 수행될 때 데이터베이스의 일관성과 무결성을 유지.
-- Lua 스크립트를 이용한 토큰 버킷 알고리즘 구현

-- Redis 키
local key = KEYS[1]
-- 버킷의 최대 용량
local capacity = tonumber(ARGV[1])
-- 리필되는 토큰 수
local refill_tokens = tonumber(ARGV[2])
-- 토큰이 리필되는 간격 (초 단위)
local refill_interval = tonumber(ARGV[3])
-- 현재 시간 (초 단위)
local current_time = tonumber(ARGV[4])

-- 현재 토큰 수와 마지막 리필 시간을 Redis에서 가져옴
local bucket = redis.call('HMGET', key, 'tokens', 'last_refill_time')
local tokens = tonumber(bucket[1])
local last_refill_time = tonumber(bucket[2])

-- 버킷이 존재하지 않는 경우 초기화
if tokens == nil then
    tokens = capacity
    last_refill_time = current_time
end

-- 토큰 리필
local elapsed = current_time - last_refill_time
if elapsed >= refill_interval then
    local new_tokens = math.min(capacity, tokens + math.floor(elapsed / refill_interval) * refill_tokens)
    tokens = new_tokens
    last_refill_time = current_time
end

-- 토큰이 충분한지 확인
if tokens > 0 then
    tokens = tokens - 1
    redis.call('HMSET', key, 'tokens', tokens, 'last_refill_time', last_refill_time)
    return 1 -- 요청 허용
else
    redis.call('HMSET', key, 'tokens', tokens, 'last_refill_time', last_refill_time)
    return 0 -- 요청 거부
end

 

 

 

 

Rate Limiter 라이브러리

1. Bucket4j

  • lock-free한 구현으로 멀티 스레딩 환경에서의 확장성이 우수하며, 추가적인 동시성 전략도 제공
  • 고성능과 높은 동시성이 요구되는 시스템에 적합

2. Guava

  • 토큰 버킷 알고리즘 기반 구현
  • guava 자체는 처리율 제한 목적은 아님 (기능은 존재)
  • 단일 인스턴스에서 속도 제한을 적용할 경우, 간단한 프로젝트에 적합
  • 동시성 제어가 약하고, 분산 시스템에 부적합

3. RateLimitJ

  • Sliding Window 알고리즘 기반 구현
  • 더 이상 지원하지 않음 (EOL)

4. Resilience4j

  • MSA 구축 목적 라이브러리
  • Java17 이상 (Spring Boot3) 부터만 사용 가능  

번외. Spring Cloud Gateway (API Gateway)

  • API 게이트웨이로서 레이트 리미팅, 로드 밸런싱, 회로 차단 등의 기능을 제공
  • Spring Boot 기반의 프로젝트에서 사용 가능

 

Custom Redis Rate Limiter (Token Bucket)

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

@Service
public class TokenBucket extends RateLimiter {
    private int tokens; // 현재 토큰 수
    private final int capacity; // 버킷의 최대 용량
    private long lastRefillTime; // 마지막으로 토큰이 리필된 시간
    private final RedisTemplate<String, Object> redisTemplate; // Redis 템플릿
    private final String redisKey; // Redis에서 사용할 키

    @Autowired
    public TokenBucket(int maxRequestPerSec, RedisTemplate<String, Object> redisTemplate, String key) {
        super(maxRequestPerSec);
        this.tokens = maxRequestPerSec;
        this.capacity = maxRequestPerSec;
        this.lastRefillTime = scaledTime();
        this.redisTemplate = redisTemplate;
        this.redisKey = key + ":tokens";
        // Redis에 초기 토큰 수 설정 (설정되어 있지 않은 경우)
        if (redisTemplate.opsForValue().get(this.redisKey) == null) {
            redisTemplate.opsForValue().set(this.redisKey, this.tokens, 1, TimeUnit.HOURS); // 예시로 1시간 TTL 설정
        }
    }

    @Override
    public boolean allow() {
        synchronized (this) {
            refillTokens(); // 토큰 리필
            // Redis에서 현재 토큰 수를 가져옴
            Integer currentTokens = (Integer) redisTemplate.opsForValue().get(this.redisKey);
            if (currentTokens == null || currentTokens == 0) {
                return false; // 토큰이 없으면 요청 거부
            }
            // Redis에서 토큰 수를 감소
            redisTemplate.opsForValue().decrement(this.redisKey);
            return true; // 요청 허용
        }
    }

    private void refillTokens() {
        final long now = scaledTime(); // 현재 시간을 가져옴
        if (now > this.lastRefillTime) {
            final double elapsedTime = (now - this.lastRefillTime);
            int refill = (int) (elapsedTime * this.maxRequestPerSec);
            // Redis에서 현재 토큰 수를 가져옴
            Integer currentTokens = (Integer) redisTemplate.opsForValue().get(this.redisKey);
            if (currentTokens == null) {
                currentTokens = this.tokens;
            }
            // 새로운 토큰 수를 계산
            int newTokenCount = Math.min(currentTokens + refill, this.capacity);
            // Redis에 토큰 수를 업데이트
            redisTemplate.opsForValue().set(this.redisKey, newTokenCount, 1, TimeUnit.HOURS); // 예시로 1시간 TTL 설정
            this.lastRefillTime = now; // 마지막 리필 시간을 업데이트
        }
    }

    private long scaledTime() {
        return System.currentTimeMillis() / 1000; // 현재 시간을 초 단위로 반환
    }
}

 

 

5가지 라이브러리 중, 확장성이 가장 훌륭한 Bucket4J를 살펴보자.

특히 토큰 버킷 알고리즘을 쓰기 때문에, 일정한 속도로 토큰을 추가하고 요청 시 토큰을 소모하는 방식으로 작동한다.
lock-free 상태에서 필요한 경우에 사용할 수 있는 동시성 전략을 제공하고, 레퍼런스 수가 가장 많았다.

https://www.baeldung.com/spring-bucket4j

 

Bucket4J Class

Refill : 일정시간마다 몇개의 Token을 충전할지 지정하는 클래스

Bandwidth : Bucket의 총 크기를 지정하는 클래스 (Token 충전 주기 및 개수를 지정한 Refill 클래스 사용하여 만듦)

Bucket  : 최종적으로 트래픽 제어를 할 클래스(Bandwidth 클래스를 사용하여 빌드)

import com.github.bucket4j.Bandwidth;
import com.github.bucket4j.Bucket;
import com.github.bucket4j.Bucket4j;
import com.github.bucket4j.Refill;
import com.github.bucket4j.redis.lettuce.cas.LettuceBasedProxyManager;

@Service
public class RateLimiterService {
    private RedisClient redisClient;
    private StatefulRedisConnection<String, String> connection;
    private LettuceBasedProxyManager<String> buckets;
    private final Map<String, Bucket> cache = new ConcurrentHashMap<>();

    @PostConstruct
    public void init() {
        redisClient = RedisClient.create("redis://localhost:6379");
        connection = redisClient.connect();
        buckets = new LettuceBasedProxyManager<>(connection.sync());
    }

    // API별로 다른 버킷을 생성하는 메서드
    private Bucket createBucketForApi(String api) {
        switch (api) {
            case "api1":
                return Bucket4j.builder()
                        .addLimit(Bandwidth.classic(10, Refill.greedy(10, Duration.ofMinutes(1))))
                        .build();
            case "api2":
                return Bucket4j.builder()
                        .addLimit(Bandwidth.classic(20, Refill.greedy(20, Duration.ofMinutes(5))))
                        .build();
            default:
                return Bucket4j.builder()
                        .addLimit(Bandwidth.classic(5, Refill.greedy(5, Duration.ofMinutes(1))))
                        .build();
        }
    }

    // API별로 요청이 허용되는지 확인하는 메서드
    public boolean isAllowed(String api, String key) {
        String bucketKey = api + ":" + key;
        Bucket bucket = cache.computeIfAbsent(bucketKey, k -> buckets.builder().build(bucketKey, () -> createBucketForApi(api)));
        return bucket.tryConsume(1);
    }
}
@RestController
@RequestMapping("/api")
public class ApiController {
    private final RateLimiterService rateLimiterService;

    @Autowired
    public ApiController(RateLimiterService rateLimiterService) {
        this.rateLimiterService = rateLimiterService;
    }

    // API1 엔드포인트
    @GetMapping("/resource1")
    public ResponseEntity<String> getResource1(@RequestParam String clientId) {
        boolean allowed = rateLimiterService.isAllowed("api1", clientId);

        if (!allowed)
            return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).body("Too many requests for API1");

        return ResponseEntity.ok("Request accepted for API1");
    }
}

 

 

 

클라이언트에게 자신의 요청이 제한에 걸리고 있는지 알려주자

1. Http response header를 활용한다.

  • X-Ratelimit-Remaining: 윈도 내 남은 처리가능 요청 수
  • X-Ratelimit-Limit: 매 윈도마다 클라이언트가 전송할 수 있는 요청의 수
  • X-Ratelimit-Retry-After: 한도 제한에 걸리지 않으려면 몇 초 뒤에 요청을 보내야하는 지 알림

2. API Response body로 알려주자

 

Result

  • 일정 기간 동안의 최대 요청 수 제어가 가능해졌다.
  • API 요청 대기로 인한 타임아웃 문제를 사전 방지할 수 있게 되었다.

 

 

왜 애플리케이션 단에서 Rate Limit 처리를 했는가?

위치 장점 단점
클라이언트   - 위변조 쉬움
- 요청 통제 쉽지 않음
미들웨어 - API Gateway 지원되기 때문에 구현 용이
- MSA 환경 추천
- 커스텀의 한계
- API Gateway에 이슈 발생시 전체 처리율 제한에 영향 가능성
서버 - Rate Limit 알고리즘 선택 가능
- Rate Limit 로직 구현 필요
- 사용 중인 PGL이 서버측 구현을 지원할 만큼 효율이 높은지 검토가 필요

 

주도적으로 애플리케이션 내에서 주도적으로 Rate Limit 관리가 가능하다는점이 가장 컸다.

예를 들어 상황에 맞는 Rate Limit의 알고리즘을 직접 선택할 수 있고, 커스텀이 용이(ex. 사용자의 요청 수를 보고 계정을 제제시킨다던지, 적절한 에러 메시지 처리) 하다는 점이었다.

라이브러리도 워낙 잘 나와있어서 적용하기 너무 쉬움 

 

 

애플리케이션 앞단에서 Rate Limit 처리 할 경우

1. API Gateway

API Gateway는 여러 서비스나 API에 대한 단일 진입점 역할을 하는 서버이다. API Gateway는 클라이언트 요청을 수신하고, 이를 적절한 서비스로 라우팅하는 기능을 제공한다고 한다.

  1. 라우팅: 클라이언트 요청을 적절한 서비스나 API로 라우팅
  2. 인증 및 권한 부여: 클라이언트 요청을 인증하고, 권한을 부여
  3. 로드 밸런싱: 여러 서버에 요청을 분산시켜 부하를 고르게 분산
  4. 속도 제한: 요청 수를 제한하여 서버 과부하를 방지
  5. 데이터 변환: 요청과 응답 데이터를 변환하여 클라이언트와 서비스 간의 통신을 용이하게 함
  6. 로깅 및 모니터링: 요청과 응답을 기록하고, 시스템 상태를 모니터링

 

2. nginx Rate Limit

nginx Rate Limit은 Leaky Bucket 알고리즘과 유사하게 동작된다.

nginx에는 요청 수를 제한하는 limit_req, 커넥션 수를 제한하는 limit_conn 모듈을 제공하고 있다.

 

1. limit_req (요청 제한)

http { 
	# 클라이언트 IP 주소를 기반으로 요청 속도를 제한하기 위한 영역 정의
	limit_req_zone $binary_remote_addr zone=request_limit_per_ip:10m rate=10r/s;

	server {
    	location / {
        	# 정의된 영역을 사용하여 요청 속도 제한 적용
        	limit_req zone=request_limit_per_ip burst=10 nodelay;
    	}
	}
}

 

2. limit_conn (커넥션 제한)

http { 
	# 서버 이름을 기반으로 연결 수를 제한하기 위한 영역 정의
	limit_conn_zone $server_name zone=request_limit_per_server:10m;

	server {
    	location / {
        	# 정의된 영역을 사용하여 각 서버의 최대 연결 수를 10으로 제한
        	limit_conn request_limit_per_server 10;
    	}
	}
}

 

 

왜 메시지 큐는 고려 대상에서 제외되었는가?

처음에 나온 아이디어는 RabbitMQ와 같은 Message Queue였다.

* 메시지 큐는 대용량 데이터를 처리하기 위한 배치 작업, 채팅 서비스, 비동기 데이터를 처리할 때 사용된다.

사용자가 많아지거나 데이터가 많아지면 응답 지연으로 서비스가 정상적으로 작동하지 못하는 상황이 올 수 있기 때문에, 기존에 분산되어 있던 데이터 처리를 한 곳에 집중하면서 메시지 브로커를 통해 필요한 프로그램에 작업을 분산시킨다.

 

메시지 큐는 비동기 작업 처리와 분산 시스템에서의 트래픽 제어에 효과적이지만, 이번 문제를 해결하는 데 적합하지 않았다.

 

1. 실시간 요청 제어의 필요성: 가장 큰 원인이었다. 메시지 큐를 사용하면 요청을 큐에 넣고 비동기적으로 처리하게 되어, 사용자에게 즉각적인 응답을 제공하기 어렵고, 실시간 API 요청 수 관리가 불가하기 때문에 기존 기획 의도와는 방향성이 틀어질 수 있었다.

 

2. 복잡성 증가: 프로모션이 얼마 남지 않은 상태였기 때문에, RabbitMQ를 탑재해서 큐에 메시지를 넣고 꺼내는 작업, 메시지 처리의 신뢰성 보장, 큐 관리 등 Rate Limiter에 비해 공수가 많이 든다고 판단했다.

 

 

참고

 

라라벨 8.x - 속도 제한

라라벨 한글 메뉴얼 8.x - 속도 제한

laravel.kr

 

 

라라벨 8.x - 라우팅

라라벨 한글 메뉴얼 8.x - 라우팅

laravel.kr

 

[개념원리]Message Queue

Message Queue에 대한 개념 및 여러 오픈소스에 대해 설명합니다.

velog.io

 

[Spring Boot] Bucket4j를 이용해 서버 측 처리율 제한 장치 구축하기

잦은 제 3자의 서비스를 호출하여 과금이 나올까봐 무섭다면? 처리율 제한 장치를 구축할 것!

velog.io

 

[Nginx RateLimit] Nginx에 RateLimit 적용해보기

Rate Limiting주어진 특정한 시간 동안 제한된 HTTP 요청량을 제한할 수 있도록 하는 기능.Rate Limiting을 하는 이유보안 측면의 목적 (Brute force attack 방지 등)비정상적인 요청 방지 (DDoS 공격 등 방지)

willseungh0.tistory.com

 

[Spring Boot] 자바 스프링에서 처리율 제한 기능을 구현하는 4가지 방법

개요 스마트폰 어플에서 우다다다 버튼을 누르다보면 가끔 아래와 같은 메세지를 마주할 수 있다. 이런 기능들은 대부분의 사이트에서 만나볼 수 있는데, 보통 서버 안정을 위해 필수적으로 탑

hogwart-scholars.tistory.com

 

 

[Rate Limit - step 1] Rate Limit이란? (소개, Throttling, 구현시 주의사항 등)

필자는 현재 API서버를 열심히 개발 하고 있다. 개발한 서비스가 트래픽을 많이 받다보니, 트래픽을 적절히 제한하지 않았을 때 장애가 발생했고, 그에 따라 API의 Rate Limit을 구현해야 했다. 공부

etloveguitar.tistory.com

 

서비스 가용성 확보에 필요한 Rate Limiting Algorithm에 대해 | Mimul Tech log

Rate Limit 알고리즘(Leaky bucket, Token bucket, Fixed window counter, Sliding window log, Sliding window counter)과 주요 서비스들의 Rate Limit 정책 등에 정리한 글.

www.mimul.com