1. 程式人生 > >限流模式-Guava的RateLimiter

限流模式-Guava的RateLimiter

目前有幾種常見的限流方式:
1、通過限制單位時間段內呼叫量來限流
2、通過限制系統的併發呼叫程度來限流
3、使用漏桶(Leaky Bucket)演算法來進行限流
4、使用令牌桶(Token Bucket)演算法來進行限流

具體我們看下第三種和第四中演算法,也是我們目前看到的最常見的限流演算法
漏桶(Leaky Bucket)演算法思路很簡單,水(請求)先進入到漏桶裡,漏桶以一定的速度出水(介面有響應速率),當水流入速度過大會直接溢位(訪問頻率超過介面響應速率),然後就拒絕請求,可以看出漏桶演算法能強行限制資料的傳輸速率.示意圖如下:

可見這裡有兩個變數,一個是桶的大小,支援流量突發增多時可以存多少的水(burst),另一個是水桶漏洞的大小(rate),虛擬碼如下:

double rate;               // leak rate in calls/s
double burst;              // bucket size in calls

long refreshTime;          // time for last water refresh
double water;              // water count at refreshTime

refreshWater() {
    long  now = getTimeOfDay();
    
    //水隨著時間流逝,不斷流走,最多就流乾到0.
    water = max(0
, water- (now - refreshTime)*rate); refreshTime = now; } bool permissionGranted() { refreshWater(); if (water < burst) { // 水桶還沒滿,繼續加1 water ++; return true; } else { return false; } }

漏桶演算法其實是悲觀的,因為它嚴格限制了系統的吞吐量,從某種角度上來說,它的效果和併發量限流很類似。漏桶演算法也可以用於大多數場景,但由於它對服務吞吐量有著嚴格固定的限制,如果在某個大的服務網路中只對某些服務進行漏桶演算法限流,這些服務可能會成為瓶頸。其實對於可擴充套件的大型服務網路,上游的服務壓力可以經過多重下游服務進行擴散,過多的漏桶限流似乎意義不大。


 

令牌桶演算法(Token Bucket)和 Leaky Bucket 效果一樣但方向相反的演算法,更加容易理解.隨著時間流逝,系統會按恆定1/QPS時間間隔(如果QPS=100,則間隔是10ms)往桶裡加入Token(想象和漏洞漏水相反,有個水龍頭在不斷的加水),如果桶已經滿了就不再加了.新請求來臨時,會各自拿走一個Token,如果沒有Token可拿了就阻塞或者拒絕服務.

令牌桶的另外一個好處是可以方便的改變速度. 一旦需要提高速率,則按需提高放入桶中的令牌的速率. 一般會定時(比如100毫秒)往桶中增加一定數量的令牌, 有些變種演算法則實時的計算應該增加的令牌的數量.

ratelimiter的簡介:Google開源工具包Guava提供了限流工具類RateLimiter,該類基於令牌桶演算法(Token Bucket)來完成限流,非常易於使用.RateLimiter經常用於限制對一些物理資源或者邏輯資源的訪問速率.它支援兩種獲取permits介面,一種是如果拿不到立刻返回false,一種會阻塞等待一段時間看能不能拿到.RateLimiter和Java中的訊號量(java.util.concurrent.Semaphore)類似,Semaphore通常用於限制併發量.

我們先看看如何建立一個RateLimiter例項:

RateLimiter create(double permitsPerSecond);  // 建立一個每秒包含permitsPerSecond個令牌的令牌桶,可以理解為QPS最多為permitsPerSecond

RateLimiter create(double permitsPerSecond, long warmupPeriod, TimeUnit unit)// 建立一個每秒包含permitsPerSecond個令牌的令牌桶,可以理解為QPS最多為permitsPerSecond,幷包含某個時間段的預熱期

我們再看看獲取令牌的相關方法:

double acquire(); // 阻塞直到獲取一個許可,返回被限制的睡眠等待時間,單位秒

double acquire(int permits); // 阻塞直到獲取permits個許可,返回被限制的睡眠等待時間,單位秒

boolean tryAcquire();// 嘗試獲取一個許可

boolean tryAcquire(int permits);// 嘗試獲取permits個許可

boolean tryAcquire(long timeout, TimeUnit unit);// 嘗試獲取一個許可,最多等待timeout時間

boolean tryAcquire(int permits, long timeout, TimeUnit unit);// 嘗試獲取permits個許可,最多等待timeout時間

我們來看個最簡單的例子:

SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd HH:mm:ss.SSS");

RateLimiter rateLimiter = RateLimiter.create(2);

while(true) {

rateLimiter.acquire();

System.out.println(simpleDateFormat.format(new Date()));

}

執行該例子會得到類似如下結果:

20170323 17:04:03.352

20170323 17:04:03.851

20170323 17:04:04.350

20170323 17:04:04.849

20170323 17:04:05.350

20170323 17:04:05.850

20170323 17:04:06.350

20170323 17:04:06.850

我們從中看到,我們在開始設定的QPS是2,也就是說每秒2個請求,在我們列印的結果中每次結果相隔就是500毫秒。

在ratelimiter中,有兩種演算法:一種是平滑演算法(SmoothBursty)一種是預熱型演算法(SmoothWarmingUp),他們之間最顯著的區別就是ratelimiter的子方法:

/重設流量相關引數,需要子類來實現,不同子類引數不盡相同,比如SmoothWarmingUp肯定有增長比率相關引數

void doSetRate(double permitsPerSecond, double stableIntervalMicros);

/計算生成這些許可數需要等待的時間

long storedPermitsToWaitTime(double storedPermits, double permitsToTake);

/返回許可冷卻(間隔)時間

double coolDownIntervalMicros();

其中,storedPermitsToWaitTime這個決定了兩種方法的等待時間計算方式的不一樣,首先smoothburst的該方法是返回0,也就是說對於smoothburst而言,他裡面的令牌會一次性全部給請求,所以他的等待時間=缺少的令牌數×stableIntervalMicros(固定的微妙數),而smoothwarmingup的時間計算方式不是這樣的,而是緩慢釋放令牌直到threshold,再進行QPS的速度,如下圖:

*          ^ throttling
 *          |
 * 3*stable +                  /
 * interval |                 /.
 *  (cold)  |                / .
 *          |               /  .   <-- "warmup period" is the area of the trapezoid between
 * 2*stable +              /   .       halfPermits and maxPermits
 * interval |             /    .
 *          |            /     .
 *          |           /      .
 *   stable +----------/  WARM . }
 * interval |          .   UP  . } <-- this rectangle (from 0 to maxPermits, and
 *          |          . PERIOD. }     height == stableInterval) defines the cooldown period,
 *          |          .       . }     and we want cooldownPeriod == warmupPeriod
 *          |---------------------------------> storedPermits
 *              (halfPermits) (maxPermits)
 *
具體的演算法,是按照從右到左計算每次獲取的permits×(interval1+interval2)/2,簡單來說就是計算從左到右覆蓋的面積(開始是梯形面積計算)

還有一個點需要重點說明下:我們可以看下我們獲取的每次等待時間

0.0

20170324 14:05:24.201

0.497167

20170324 14:05:24.700

0.499216

20170324 14:05:25.199

0.50035

20170324 14:05:25.699

為什麼第一次是0呢?這是因為我們看我們返回的時間值的方法:
void resync(long nowMicros) {
  // if nextFreeTicket is in the past, resync to now
if (nowMicros > nextFreeTicketMicros) {
    double newPermits = (nowMicros - nextFreeTicketMicros) / coolDownIntervalMicros();
storedPermits = min(maxPermits, storedPermits + newPermits);
nextFreeTicketMicros = nowMicros;
}
}
也就是說如果當前時間大於下一次預測時間,等待時間為0,因為初始化nextfreeticketmicros=0,所以返回的returnvalue=0,同時會在預測下一次獲取令牌的時間,方法為:
long waitMicros =
    storedPermitsToWaitTime(this.storedPermits, storedPermitsToSpend)
        + (long) (freshPermits * stableIntervalMicros);
this.nextFreeTicketMicros = LongMath.saturatedAdd(nextFreeTicketMicros, waitMicros);

相關推薦

關於兩種模式

方案 次數 進行 其他 att 分析 常用 服務 技術 流量預警和限流方案中,比較常用的有兩種。第一種滑窗模式,通過統計一段時間內的訪問次數來進行控制,訪問次數達到的某個峰值時進行限流。第二種為並發用戶數模式,通過控制最大並發用戶數,來達到流量控制的目的。下面來簡單分析下兩

模式【其他模式

限流模式 @Slf4j public class Throttling { /** * 限流模式: * Ensure that a given client is not able to access service resources * more than the

模式-Guava的RateLimiter

目前有幾種常見的限流方式: 1、通過限制單位時間段內呼叫量來限流 2、通過限制系統的併發呼叫程度來限流 3、使用漏桶(Leaky Bucket)演算法來進行限流 4、使用令牌桶(Token Bucket)演算法來進行限流 具體我們看下第三種和第四中演算法,也是我們目前看

五指cms 欄目訪問權和內容訪問權-提醒模式配置

五指cms wuzhicms系統模式:沒有權限時直接提醒手動模式:可以在模版中,隱藏沒有權限訪問的內容。靈活性更高。 在模版中可以取到變量:$access_authority2種值:true or false為true時,有權限為false時,無權限<div class="content-p"&

、消峰的三種辦法

鏈接 png ret unit 做了 pan mit append vertex 互聯網服務賴以生存的根本是流量, 產品和運營會經常通過各種方式來為應用倒流,比如淘寶的雙十一等,如何讓系統在處理高並發的同時還是保證自身系統的穩定,通常在最短時間內提高並發的做法就是加機器,

Nginx (請求數)

pan rst 沒有 con 空間大小 限流 ssi 請求 status limit_req_zone 用於設置每個IP在單位時間內所允許發起的請求數,值 zone=rate=10r/s 表示每個IP每秒只允許發起10個請求。limit_req的作用類似與緩沖區,用於緩存還

並發、、緩存

mit 異步處理 返回 let ble off 等待 限流 builder 1.並發 使用CompletableFuture可以控制並發,並且等待所有異步處理完成後返回allOff; 2.限流 使用guava中的RateLimiter可以控制秒級限流。 3.緩存 使

How to setup Assigned Access in Windows 10 (Kiosk Mode) 設置分配的訪問權(Kiosk模式)

win tar mode ctr assigned all oos rsquo eve Let’s say you’re building some sort of ingenious mechanical contraption to be dis

常用的算法

緩沖 bucket 允許 icp 流量整形 使用計數 時間段 線程池 進行   常用的限流算法大致有三種:令牌桶算法,漏桶算法,計數器算法 令牌桶算法   令牌桶算法是一個存放固定容量令牌的桶,按照固定速率往桶裏添加令牌。令牌桶算法的描述如下:   1.假設限制2r/s,則

降級設置

task lin 緩存設計 image 活動 關閉 拒絕 生效 varchar cdn的收費方式按最高點來收取,為了節省成本,需要在cdn的流量到達最大成本值前通過服務器程序限制客戶端的請求流量 需求分析 cdn的流量請求來源有: 靜默升級 用戶點擊下載 一些活動圖片資源

高並發系統之特技

有一種 mic jedis .net cep 防止 方法 框架 ise 在開發高並發系統時有三把利器用來保護系統:緩存、降級和限流。緩存的目的是提升系統訪問速度和增大系統能處理的容量,可謂是抗高並發流量的銀彈;而降級是當服務出問題或者影響到核心流程的性能則需要暫時屏蔽掉,待

nginx的問題

host 超時 sock 客戶 服務器 tle style index 代碼 http{ limit_req_zone $binary_remote_addr zone=req_one:10m rate=100r/s;server{ listen 8080;serve

簡單裝飾模式

畢業 rri 構造方法 是不是 asc implement 耦合 new ase public class Test2 { /** * 裝飾模式 * 畢業學生 */ public static void main(String[] args) { HeiMaStu

借助Redis做秒殺和的思考

rlock 會有 如果 小數據 更多 準備 while col x86 最近群裏聊起秒殺和限流,我自己沒有做過類似應用,但是工作中遇到過更大的數據和並發。 於是提出了一個簡單的模型: var count = rds.inc(key); if(count > 10

Nginx如何實現讀寫的方法

http height normal radi wrap border ret ola style 針對Nginx請求,單個IP,每秒50讀次,寫10次。萬能的Nginx,幾行配置搞定# 先定義好規則,需要寫在server外面 limit_req_zone $binary_

Django Rest framework的實現流程

分鐘 例如 ttl 長度 後繼 控制 ron response 臨時 目錄 一 什麽是throttle 二 Django REST framework是如何實現throttle的 三 Django REST framework中throttle源碼流程 一 什麽是throt

算法

被拒絕 body span 是否 滿了 簡單 令牌 限流 限制 常見的限流算法有:令牌桶、漏桶、計數器。 令牌桶限流 令牌桶是一個存放固定容量令牌的桶,按照固定速率往桶裏添加令牌,填滿了就丟棄令牌,請求是否被處理要看桶中令牌是否足夠,當令牌數減為零時則拒絕新的請求。令牌桶允

分布式環境 解決方案

OS 訪問 right -c 拒絕 key ews 可行性分析 聲明 業務背景介紹 對於web應用的限流,光看標題,似乎過於抽象,難以理解,那我們還是以具體的某一個應用場景來引入這個話題吧。 在日常生活中,我們肯定收到過不少不少這樣的短信,“雙11約嗎?,千款…

TCP通過滑動窗口和擁塞窗口實現,能抵禦ddos攻擊嗎

沒有 arc 算法實現 https 占滿 %20 ddos攻擊 ddos www   tcp可以通過滑動窗口和擁塞算法實現流量控制,限制上行和下行的流量,但是卻不能抵禦ddos攻擊。   限流只是限制訪問流量的大小,是無法區分正常流量和異常攻擊流量的。   限流可以控制本軟

用nginx實現分布式(防DDOS攻擊)

第一步 root mapping nts LV web ons -o plain 1.前言 一般對外暴露的系統,在促銷或者黑客攻擊時會湧來大量的請求,為了保護系統不被瞬間到來的高並發流量給打垮, 就需要限流 . 本文主要闡述如何用nginx 來實現限流. 聽說