1. 程式人生 > >Guava的RateLimiter限流器分析

Guava的RateLimiter限流器分析

RateLimiter

  • 演算法原理

    • RateLimiter 從概念上來講,速率限制器會在可配置的速率下分配許可證。如果必要的話,每個acquire() 會阻塞當前執行緒直到許可證可用後獲取該許可證。一旦獲取到許可證,不需要再釋放許可證。

  • 使用案例

    • RateLimiter.create(10);
    • RateLimiter.acquire();
  • 有兩個實現子類

    • SmoothBursty

      • acquire後立即開始
    • SmoothWarmingUp

      • 有一段warm up間隔,後再開始

原始碼解析(SmoothBursty)

建立令牌桶

* RateLimiter.create(permitsPerSecond);

    * permitsPerSecond : 每一秒發放的令牌數

核心引數

  • maxBurstSeconds

    • 統計的時間範圍,預設為1,即每一秒內發放的令牌
  • nextFreeTicketMicros

    • 下一個令牌可以獲取的時間點,單位微秒
  • storedPermits

    • 已有的令牌數
  • maxPermits

    • 最大的令牌數

    • maxPermits = permitsPerSecond * maxBurstSeconds

  • stableIntervalMicros

    • 令牌之間的平均間隔時間,單位微秒

    • stableIntervalMicros = 1秒 / permitsPerSecond

原始碼分析

public static RateLimiter create(double permitsPerSecond) {
    return create(SleepingStopwatch.createFromSystemTimer(), permitsPerSecond);
}
static RateLimiter create(SleepingStopwatch stopwatch, double permitsPerSecond) {
    //設定時間範圍長度
    RateLimiter rateLimiter = new SmoothBursty(stopwatch, 1.0 /* maxBurstSeconds */);
    rateLimiter.setRate(permitsPerSecond);
    return rateLimiter;
}
public final void setRate(double permitsPerSecond) {
    checkArgument(
        permitsPerSecond > 0.0 && !Double.isNaN(permitsPerSecond), "rate must be positive");
    //互斥鎖
    synchronized (mutex()) {
      //設定令牌桶頻率
      doSetRate(permitsPerSecond, stopwatch.readMicros());
    }
}
final void doSetRate(double permitsPerSecond, long nowMicros) {
    //更新引數
    resync(nowMicros);
    //令牌平均間隔時間
    double stableIntervalMicros = SECONDS.toMicros(1L) / permitsPerSecond;
    this.stableIntervalMicros = stableIntervalMicros;
    doSetRate(permitsPerSecond, stableIntervalMicros);
}
void doSetRate(double permitsPerSecond, double stableIntervalMicros) {
  double oldMaxPermits = this.maxPermits;
  //最大令牌數
  maxPermits = maxBurstSeconds * permitsPerSecond;
  //設定令牌桶的已有令牌數,初始狀態時為0
  if (oldMaxPermits == Double.POSITIVE_INFINITY) {
    // if we don't special-case this, we would get storedPermits == NaN, below
    storedPermits = maxPermits;
  } else {
    storedPermits = (oldMaxPermits == 0.0)
        ? 0.0 // initial state
        : storedPermits * maxPermits / oldMaxPermits;
  }
}
//更新引數,入參為當前時間
void resync(long nowMicros) {
    // 如果當前時間超過下一個令牌可獲取時間,更新引數,並且將下個令牌的可獲取時間更新為當前時間
    if (nowMicros > nextFreeTicketMicros) {
      //更新令牌桶裡儲存的令牌數,包括 nowMicros - nextFreeTicketMicros 時間範圍內補充的令牌數
      storedPermits = min(maxPermits,
          storedPermits
            + (nowMicros - nextFreeTicketMicros) / coolDownIntervalMicros());
      nextFreeTicketMicros = nowMicros;
    }

}

獲取令牌

  • rateLimiter.acquire(int permits)

    • permits : 需要獲取的令牌數

原始碼分析

//獲取令牌
public double acquire(int permits) {
    long microsToWait = reserve(permits);
    stopwatch.sleepMicrosUninterruptibly(microsToWait);
    return 1.0 * microsToWait / SECONDS.toMicros(1L);
}

final long reserve(int permits) {
    checkPermits(permits);
    synchronized (mutex()) {
      return reserveAndGetWaitLength(permits, stopwatch.readMicros());
    }
}

//獲取令牌,並返回需要等待的時間,單位微秒
final long reserveAndGetWaitLength(int permits, long nowMicros) {
    long momentAvailable = reserveEarliestAvailable(permits, nowMicros);
    return max(momentAvailable - nowMicros, 0);
}

//獲取最近的一個可用令牌的時間
final long reserveEarliestAvailable(int requiredPermits, long nowMicros) {
    //更新引數
    resync(nowMicros);
    //記錄最近一個可用令牌的時間
    long returnValue = nextFreeTicketMicros;
    //將要花費的令牌數
    double storedPermitsToSpend = min(requiredPermits, this.storedPermits);
    //未花費掉的令牌數
    double freshPermits = requiredPermits - storedPermitsToSpend;
    //需要等待的時間,未花費的令牌數*令牌間隔時間
    long waitMicros = storedPermitsToWaitTime(this.storedPermits, storedPermitsToSpend)
        + (long) (freshPermits * stableIntervalMicros);

    try {
        //更新下個可用令牌的可獲取時間,最近可用令牌的時間+需要等待的時間
        this.nextFreeTicketMicros = LongMath.checkedAdd(nextFreeTicketMicros, waitMicros);
    } catch (ArithmeticException e) {
        this.nextFreeTicketMicros = Long.MAX_VALUE;
    }
    //更新令牌桶裡的令牌數
    this.storedPermits -= storedPermitsToSpend;
    return returnValue;
}
long storedPermitsToWaitTime(double storedPermits, double permitsToTake) {
  return 0L;
}

相關推薦

Guava的RateLimiter分析

RateLimiter 演算法原理 RateLimiter 從概念上來講,速率限制器會在可配置的速率下分配許可證。如果必要的話,每個acquire() 會阻塞當前執行緒直到許可證可用後獲取該許可證。一旦獲取到許可證,不需要再釋放許可證。 使用案例 Ra

基於Redis的INCR實現一個

spa 自減 簡單 onu intern rate 第一次 轉化 cti 模式:計數器 計數器是 Redis 的原子性自增操作可實現的最直觀的模式了,它的想法相當簡單:每當某個操作發生時,向 Redis 發送一個 INCR 命令。 比如在一個 web 應用程序中,如果想知

google的RateLimiter的使用

背景:A系統需要呼叫B,C,D系統,B,C,D系統沒有能力做限流,因此需要A系統針對B,C,D系統做限流,每秒傳送物件要求的請求數。可以使用下面的元件進行控制 import com.google.common.cache.Cache; import com.google.c

API (一):基於redis的API訪問頻率控制器的實現

 在open API日益盛行的今天,API的訪問頻率控制尤為重要。Google開源的Guava中有個類叫RateLimiter,但是此類控制粒度只是秒級別的,沒有提供分鐘,小時,天級別的限制,而且採取的是阻塞的模式,應用起來不是很方便。本人依據Redis的有序集合開發了一個訪

使用Guava實現

為什麼需要限流?在開發高併發系統時有三把利器用來保護系統:快取、降級和限流。限流可以認為服務降級的一種,限流通過限制請求的流量以達到保護系統的目的。一般來說,系統的吞吐量是可以計算出一個閾值的,為了保證系統的穩定執行,一旦達到這個閾值,就需要限制流量並採取一些措施以完成限制流

API (三) 在Spring Cloud 微服務體系中整合RedisRateLimiter

  這篇是API限流器這個系列的終章,就是講述如何在Spring Cloud 微服務開發中應用我發明的先進限流器。開篇明義,基本思路如下:1. 定義一個annotation - RedisLimiter2. 在RestController 中有URL Mapping 的方法上

API (二)史上最優秀的訪問頻率控制器的演算法設計詳解

專案程式碼地址: https://github.com/tangaiyun/RedisRateLimiter演算法的核心思想是用redis中的有序集合來記錄訪問痕跡,集合的key與時間段序號強相關,集合中加入訪問痕跡元素時,score值和member都來自於redis的當前精

基於Redis的的實現

1 概述 系統中的介面通常都有限流,比如 70次/秒 ,如何保證我們的介面的呼叫次數在超過第三方介面限流的時候快速失敗呢?這時候就需要限流器了。下面是筆者用redis實現限流器的流程圖。 2 程

redis+lua實現分散式令牌桶

redis+lua分散式令牌桶限流器 使用redis執行lua指令碼,返回結果為0表示被限流,為1表示正常訪問。lua指令碼傳入的KEYS[1]為redis中配置限流器qps的map的key,KEYS[2]為限流器的key,KEY[3]為秒級時間戳;傳入的AR

分散式系統演算法分析與實現

一、限流的關鍵作用 對於大型網際網路架構中,限流的設計是必不可少的一個環節。在給定的時間內, 客戶端請求次數過多, 伺服器就會攔截掉部分請求,避免請求流量過大造成資料庫負載高的問題。   二、常見限流演算法利弊分析 計數器限流 計數器限流主要有固定視窗計數器和滑動視窗計數器。固定視窗計數器即:在單位

請你講講分散式系統中的一般如何實現?

### 限流器相關演算法 一般限流器有五種演算法,分別是:令牌桶,漏斗桶,固定視窗,滑動日誌(指的其實是廣義上的滑動視窗),滑動視窗(**這裡指的是滑動日誌+固定視窗結合的一種演算法**)。 #### 1. 令牌桶(Token bucket) 令牌桶演算法用來控制一段時間內傳送到網路上的資料的數目,並允

Java編程——服務設計方案之應用

就是 常見 一次 一定的 處理 調用 針對 tro 阻塞 前言 在一個高並發系統中對流量的把控是非常重要的,當巨大的流量直接請求到我們的服務器上沒多久就可能造成接口不可用,不處理的話甚至會造成整個應用不可用。比如最近就有個這樣的需求,我作為客戶端要向kafka生產數據,而k

【.NET Core專案實戰-統一認證平臺】第七章 閘道篇-自定義客戶端

原文: 【.NET Core專案實戰-統一認證平臺】第七章 閘道器篇-自定義客戶端限流 【.NET Core專案實戰-統一認證平臺】開篇及目錄索引 上篇文章我介紹瞭如何在閘道器上增加自定義客戶端授權功能,從設計到編碼實現,一步一步詳細講解,相信大家也掌握了自定義中介軟體的開發技巧了,本篇我們將介紹如

zuul閘道

最近專案需要實現限流的功能,專案使用的是spring cloud框架,用zuul做網管模組。準備在網管層加上限流功能。   1、使用RateLimiter+filter做統一入口限流。適用單機   Guava中開源出來一個令牌桶演算法的工具類RateLimiter

springcloud2+gateway閘道配置中心2(包含熔斷,jwt認證,

下面介紹1未講完的閘道器功能    1重試功能,配置如下         這裡可以不寫實現類,採用預設的方式配置,然後傳送一個http的GET請求,試著斷開服務端檢視後臺:  證明配置正確,起作用了!

springcloud2+gateway閘道配置中心1(包含熔斷,jwt認證,

第一次我也問我老大為啥不用zuul,官網有現成的指導,老大一句話:gateway效能比zuul優化效率提升20%,zuul版本落後(2x版本的code還是用的1x的原始碼),支援webflux,整合stream流;淚奔的我忙了半天zuul,哎,換! 1,引入maven,2.0以上版本注意

使用springcloud gateway搭建閘道(分流,,熔斷)

Spring Cloud Gateway Spring Cloud Gateway 是 Spring Cloud 的一個全新專案,該專案是基於 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技術開發的閘道器,它旨在為微服務架構提供一種簡單有效的統一的 API 路

H264編碼8( H264碼打包分析(精華))

來自:https://www.cnblogs.com/lidabo/p/4602422.html H264碼流打包分析 SODB 資料位元串-->最原始的編碼資料 RBSP 原始位元組序列載荷-->在SODB的後面填加了結尾位元(RBSP trailing bits 一個bit“1”)若

定義攔截,介面防刷

一、自定義註解package com.mydre.miaosha.access;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.ann

網際網路閘道設計之演算法

騰訊王卡業務第一代閘道器設計架構如上圖所示,也許有許多人會問,nginx本身就能做網關了,為什麼還需要另外開發閘道器呢? 答案是nginx開發閘道器,線上現有成員技術背景下,條件不成熟,為了快速構建我們的微服務架構,所以我們選擇了基於spring cloud zuul做