기술나눔

학습 노트 - Redis의 토큰 버킷 알고리즘을 사용하여 분산 전류 제한 구현

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

토큰 버킷 알고리즘을 소개하기 전에 먼저 리키 버킷(Leaky Bucket) 알고리즘을 소개하겠습니다.

누출 버킷 알고리즘

Leaky Bucket 알고리즘은 데이터 유입 및 유출 속도를 제어하여 데이터 전송 속도를 제한하는 고정 용량 컨테이너 모델입니다. 누출 버킷 알고리즘의 주요 기능은 다음과 같습니다.

  • 고정 용량: Leaky Bucket의 용량은 고정되어 있으며, Bucket이 가득 차면 초과된 데이터는 폐기되거나 거부됩니다.
  • 지속적인 누출: 물통 안의 "물"(데이터)이 일정한 속도로 계속해서 흘러나옵니다.
  • 버스트 처리: 버킷이 가득 차지 않은 경우 버스트 트래픽을 빠르게 처리할 수 있지만, 버킷이 가득 차면 트래픽이 제한됩니다.

Redisson 자체는 누출 ​​버킷 알고리즘의 구현을 직접 제공하지 않습니다. Redisson에서 누출 버킷 알고리즘을 구현해야 하는 경우 다음 단계를 고려할 수 있습니다.

  1. 정렬된 집합을 사용하여 토큰 저장: Redis에 토큰을 저장하려면 정렬된 집합을 사용하세요. 각 토큰에는 점수로 타임스탬프가 있습니다.

  2. 토큰 추가하기 : 정렬된 세트에 일정한 간격으로 토큰을 추가하며, 각 토큰의 점수는 추가 시 타임스탬프가 됩니다. (예약된 작업을 사용하여 추가 가능)

  3. 토큰 받기: 요청을 보내야 하는 경우 정렬된 집합에서 토큰을 제거(또는 팝)하고 현재 시간이 토큰의 타임스탬프에 허용되는 최대 지연을 더한 값보다 큰지 확인합니다.

  4. 토큰 버리기: 현재 시간이 토큰의 타임스탬프에 최대 지연 시간을 더한 시간을 초과하는 경우 "누수"를 시뮬레이션하기 위해 토큰이 삭제됩니다.

  5. 전류 제한 논리: 정렬된 집합에 사용 가능한 토큰이 없으면 요청을 거부하거나 대기 대기열에 넣습니다.

토큰 버킷 알고리즘

토큰 버킷 알고리즘은 토큰을 생성하여 특정 속도로 데이터를 전송할 수 있는 보다 유연한 흐름 제어 알고리즘입니다. 토큰 버킷 알고리즘의 주요 기능은 다음과 같습니다.

  • 토큰 생성: 고정된 비율로 버킷에 토큰을 생성합니다.
  • 토큰 사용: 데이터 전송 시 버킷에 있는 토큰을 모두 소모해야 합니다. 토큰이 부족할 경우 데이터 전송이 지연되거나 폐기됩니다.
  • 갑작스런 능력: 토큰이 충분할 때 더 큰 트래픽을 보낼 수 있으며, 그 이후에는 토큰 생성 속도에 따라 트래픽이 제한됩니다.

Redisson 프레임워크는 토큰 버킷 알고리즘을 기반으로 전류 제한 기능을 제공합니다. RRateLimiter 인터페이스 구현. 다음은 Redisson에서 토큰 버킷 알고리즘을 사용하는 간단한 예입니다.

  1. import org.redisson.api.RRateLimiter;
  2. import org.redisson.api.RedissonClient;
  3. // 假设redissonClient已经创建并配置好连接
  4. RRateLimiter rateLimiter = redissonClient.getRateLimiter("myRateLimiter");
  5. // 配置令牌桶参数
  6. rateLimiter.trySetRate(20, RateType.OVERALL); // 总共可以处理20个请求
  7. rateLimiter.trySetRate(1, RateIntervalUnit.SECONDS); // 每秒生成1个令牌
  8. // 尝试获取一个令牌,如果成功,返回true,否则返回false
  9. boolean acquired = rateLimiter.tryAcquire();
  10. // 使用令牌执行操作
  11. if (acquired) {
  12. // 执行受限操作
  13. } else {
  14. // 处理限流逻辑,例如重试或等待
  15. }
  16. // 关闭Redisson客户端
  17. redissonClient.shutdown();