Google開(kāi)源工具包Guava提供了限流工具類RateLimiter,該類基于令牌桶算法(Token Bucket)來(lái)完成限流
RateLimiter 從概念上來(lái)講,速率限制器會(huì)在可配置的速率下分配許可證胯杭。如果必要的話髓霞,每個(gè)acquire() 會(huì)阻塞當(dāng)前線程直到許可證可用后獲取該許可證兰伤。一旦獲取到許可證端仰,不需要再釋放許可證泳猬。
注:RateLimiter使用的是一種叫令牌桶的流控算法,RateLimiter會(huì)按照一定的頻率往桶里扔令牌盗冷,線程拿到令牌才能執(zhí)行怠苔,比如你希望自己的應(yīng)用程序QPS不要超過(guò)1000,那么RateLimiter設(shè)置1000的速率后仪糖,就會(huì)每秒往桶里扔1000個(gè)令牌柑司。
RateLimiter是一個(gè)abstract類,但它提供了幾個(gè)static方法用于創(chuàng)建RateLimiter:
通過(guò)設(shè)置許可證的速率permitsPerSecond
來(lái)定義RateLimiter。在默認(rèn)配置下乓诽,許可證會(huì)在固定的速率下被分配帜羊。為了確保維護(hù)配置的速率
可能存在需要使用一個(gè)擁有預(yù)熱期的RateLimiter 的情況,通過(guò)設(shè)置熱身期(warmup period)參數(shù)鸠天。在這段時(shí)間內(nèi),每秒分配的許可數(shù)會(huì)穩(wěn)定地增長(zhǎng)直到達(dá)到穩(wěn)定的速率帐姻,如果RateLimiter在熱身期沒(méi)有足夠的請(qǐng)求(unused),則起速率會(huì)逐漸降低到冷卻狀態(tài)稠集。
/**
* 創(chuàng)建一個(gè)穩(wěn)定輸出令牌的RateLimiter奶段,保證了平均每秒不超過(guò)permitsPerSecond個(gè)請(qǐng)求
* 當(dāng)請(qǐng)求到來(lái)的速度超過(guò)了permitsPerSecond,保證每秒只處理permitsPerSecond個(gè)請(qǐng)求
*/
public static RateLimiter create(double permitsPerSecond);
/**
* 創(chuàng)建一個(gè)穩(wěn)定輸出令牌的RateLimiter剥纷,保證了平均每秒不超過(guò)permitsPerSecond個(gè)請(qǐng)求
* 還包含一個(gè)熱身期(warmup period),熱身期內(nèi)痹籍,RateLimiter會(huì)平滑的將其釋放令牌的速率加大,直到起達(dá)到最大速率
* 同樣晦鞋,如果RateLimiter在熱身期沒(méi)有足夠的請(qǐng)求(unused),則起速率會(huì)逐漸降低到冷卻狀態(tài)
*
* 設(shè)計(jì)這個(gè)的意圖是為了滿足那種資源提供方需要熱身時(shí)間蹲缠,而不是每次訪問(wèn)都能提供穩(wěn)定速率的服務(wù)的情況(比如帶緩存服務(wù),需要定期刷新緩存的)
* 參數(shù)warmupPeriod和unit決定了其從冷卻狀態(tài)到達(dá)最大速率的時(shí)間
*/
public static RateLimiter create(double permitsPerSecond, long warmupPeriod, TimeUnit unit);
提供了兩個(gè)獲取令牌的方法,不帶參數(shù)表示獲取一個(gè)令牌.如果沒(méi)有令牌則一直等待,返回等待的時(shí)間(單位為秒),沒(méi)有被限流則直接返回0.0:
public double acquire();
public double acquire(int permits);
嘗試獲取令牌,分為待超時(shí)時(shí)間和不帶超時(shí)時(shí)間兩種:
public boolean tryAcquire();
//嘗試獲取一個(gè)令牌,立即返回
public boolean tryAcquire(int permits);
public boolean tryAcquire(long timeout, TimeUnit unit);
//嘗試獲取permits個(gè)令牌,帶超時(shí)時(shí)間
public boolean tryAcquire(int permits, long timeout, TimeUnit unit);
RateLimiter方法摘要
修飾符和類型 | 方法和描述 |
---|---|
double | acquire() 從RateLimiter獲取一個(gè)許可悠垛,該方法會(huì)被阻塞直到獲取到請(qǐng)求 |
double | acquire(int permits) 從RateLimiter獲取指定許可數(shù)线定,該方法會(huì)被阻塞直到獲取到請(qǐng)求 |
static RateLimiter | create(double permitsPerSecond) 根據(jù)指定的穩(wěn)定吞吐率創(chuàng)建RateLimiter,這里的吞吐率是指每秒多少許可數(shù)(通常是指QPS确买,每秒多少查詢) |
static RateLimiter | create(double permitsPerSecond, long warmupPeriod, TimeUnit unit) 根據(jù)指定的穩(wěn)定吞吐率和預(yù)熱期來(lái)創(chuàng)建RateLimiter斤讥,這里的吞吐率是指每秒多少許可數(shù)(通常是指QPS,每秒多少個(gè)請(qǐng)求量)湾趾,在這段預(yù)熱時(shí)間內(nèi)芭商,RateLimiter每秒分配的許可數(shù)會(huì)平穩(wěn)地增長(zhǎng)直到預(yù)熱期結(jié)束時(shí)達(dá)到其最大速率。(只要存在足夠請(qǐng)求數(shù)來(lái)使其飽和) |
double | getRate() 返回RateLimiter 配置中的穩(wěn)定速率搀缠,該速率單位是每秒多少許可數(shù) |
void | setRate(double permitsPerSecond) 更新RateLimite的穩(wěn)定速率铛楣,參數(shù)permitsPerSecond 由構(gòu)造RateLimiter的工廠方法提供。 |
String | toString() 返回對(duì)象的字符表現(xiàn)形式 |
boolean | tryAcquire() 從RateLimiter 獲取許可艺普,如果該許可可以在無(wú)延遲下的情況下立即獲取得到的話 |
boolean | tryAcquire(int permits) 從RateLimiter 獲取許可數(shù)簸州,如果該許可數(shù)可以在無(wú)延遲下的情況下立即獲取得到的話 |
boolean | tryAcquire(int permits, long timeout, TimeUnit unit) 從RateLimiter 獲取指定許可數(shù)如果該許可數(shù)可以在不超過(guò)timeout的時(shí)間內(nèi)獲取得到的話,或者如果無(wú)法在timeout 過(guò)期之前獲取得到許可數(shù)的話衷敌,那么立即返回false (無(wú)需等待) |
boolean | tryAcquire(long timeout, TimeUnit unit) 從RateLimiter 獲取許可如果該許可可以在不超過(guò)timeout的時(shí)間內(nèi)獲取得到的話勿侯,或者如果無(wú)法在timeout 過(guò)期之前獲取得到許可的話,那么立即返回false(無(wú)需等待) |
方法細(xì)節(jié)
create
public static RateLimiter create(double permitsPerSecond)
根據(jù)指定的穩(wěn)定吞吐率創(chuàng)建RateLimiter缴罗,這里的吞吐率是指每秒多少許可數(shù)(通常是指QPS助琐,每秒多少查詢)。
返回的RateLimiter 確保了在平均情況下面氓,每秒發(fā)布的許可數(shù)不會(huì)超過(guò)permitsPerSecond兵钮,每秒鐘會(huì)持續(xù)發(fā)送請(qǐng)求。當(dāng)傳入請(qǐng)求速率超過(guò)permitsPerSecond舌界,速率限制器會(huì)每秒釋放一個(gè)許可(1.0 / permitsPerSecond 這里是指設(shè)定了permitsPerSecond為1.0) 掘譬。當(dāng)速率限制器閑置時(shí),允許許可數(shù)暴增到permitsPerSecond呻拌,隨后的請(qǐng)求會(huì)被平滑地限制在穩(wěn)定速率permitsPerSecond中葱轩。
參數(shù):
permitsPerSecond – 返回的RateLimiter的速率,意味著每秒有多少個(gè)許可變成有效。
拋出:
IllegalArgumentException – 如果permitsPerSecond為負(fù)數(shù)或者為0
create
public static RateLimiter create(double permitsPerSecond,long warmupPeriod,TimeUnit unit)
根據(jù)指定的穩(wěn)定吞吐率和預(yù)熱期來(lái)創(chuàng)建RateLimiter靴拱,這里的吞吐率是指每秒多少許可數(shù)(通常是指QPS垃喊,每秒多少查詢),在這段預(yù)熱時(shí)間內(nèi)袜炕,RateLimiter每秒分配的許可數(shù)會(huì)平穩(wěn)地增長(zhǎng)直到預(yù)熱期結(jié)束時(shí)達(dá)到其最大速率(只要存在足夠請(qǐng)求數(shù)來(lái)使其飽和)本谜。同樣地,如果RateLimiter 在warmupPeriod時(shí)間內(nèi)閑置不用偎窘,它將會(huì)逐步地返回冷卻狀態(tài)乌助。也就是說(shuō),它會(huì)像它第一次被創(chuàng)建般經(jīng)歷同樣的預(yù)熱期陌知。返回的RateLimiter 主要用于那些需要預(yù)熱期的資源他托,這些資源實(shí)際上滿足了請(qǐng)求(比如一個(gè)遠(yuǎn)程服務(wù)),而不是在穩(wěn)定(最大)的速率下可以立即被訪問(wèn)的資源纵诞。返回的RateLimiter 在冷卻狀態(tài)下啟動(dòng)(即預(yù)熱期將會(huì)緊跟著發(fā)生)上祈,并且如果被長(zhǎng)期閑置不用,它將回到冷卻狀態(tài)浙芙。
參數(shù):
permitsPerSecond – 返回的RateLimiter的速率登刺,意味著每秒有多少個(gè)許可變成有效。
warmupPeriod – 在這段時(shí)間內(nèi)RateLimiter會(huì)增加它的速率嗡呼,在抵達(dá)它的穩(wěn)定速率或者最大速率之前
unit – 參數(shù)warmupPeriod 的時(shí)間單位拋出:
IllegalArgumentException – 如果permitsPerSecond為負(fù)數(shù)或者為0
setRate
public final void setRate(double permitsPerSecond)
更新RateLimite的穩(wěn)定速率纸俭,參數(shù)permitsPerSecond 由構(gòu)造RateLimiter的工廠方法提供。調(diào)用該方法后南窗,當(dāng)前限制線程不會(huì)被喚醒揍很,因此他們不會(huì)注意到最新的速率;只有接下來(lái)的請(qǐng)求才會(huì)万伤。需要注意的是窒悔,由于每次請(qǐng)求償還了(通過(guò)等待,如果需要的話)上一次請(qǐng)求的開(kāi)銷敌买,這意味著緊緊跟著的下一個(gè)請(qǐng)求不會(huì)被最新的速率影響到简珠,在調(diào)用了setRate 之后;它會(huì)償還上一次請(qǐng)求的開(kāi)銷虹钮,這個(gè)開(kāi)銷依賴于之前的速率聋庵。RateLimiter的行為在任何方式下都不會(huì)被改變,比如如果 RateLimiter 有20秒的預(yù)熱期配置芙粱,在此方法被調(diào)用后它還是會(huì)進(jìn)行20秒的預(yù)熱祭玉。
參數(shù):
permitsPerSecond – RateLimiter的新的穩(wěn)定速率
拋出:
IllegalArgumentException – 如果permitsPerSecond為負(fù)數(shù)或者為0
getRate
public final double getRate()
返回RateLimiter 配置中的穩(wěn)定速率,該速率單位是每秒多少許可數(shù)春畔。它的初始值相當(dāng)于構(gòu)造這個(gè)RateLimiter的工廠方法中的參數(shù)permitsPerSecond 脱货,并且只有在調(diào)用setRate(double)后才會(huì)被更新岛都。
acquire
public double acquire()
從RateLimiter獲取一個(gè)許可,該方法會(huì)被阻塞直到獲取到請(qǐng)求蹭劈。如果存在等待的情況的話疗绣,告訴調(diào)用者獲取到該請(qǐng)求所需要的睡眠時(shí)間线召。該方法等同于acquire(1)铺韧。
返回:
time spent sleeping to enforce rate, in seconds; 0.0 if not rate-limited
執(zhí)行速率的所需要的睡眠時(shí)間,單位為妙缓淹;如果沒(méi)有則返回0
acquire
public double acquire(int permits)
從RateLimiter獲取指定許可數(shù)哈打,該方法會(huì)被阻塞直到獲取到請(qǐng)求數(shù)。如果存在等待的情況的話讯壶,告訴調(diào)用者獲取到這些請(qǐng)求數(shù)所需要的睡眠時(shí)間料仗。
參數(shù):
permits – 需要獲取的許可數(shù)
返回:
執(zhí)行速率的所需要的睡眠時(shí)間,單位為妙伏蚊;如果沒(méi)有則返回0
拋出:
IllegalArgumentException – 如果請(qǐng)求的許可數(shù)為負(fù)數(shù)或者為0
tryAcquire
public boolean tryAcquire(long timeout,TimeUnit unit)
從RateLimiter獲取許可如果該許可可以在不超過(guò)timeout的時(shí)間內(nèi)獲取得到的話立轧,或者如果無(wú)法在timeout 過(guò)期之前獲取得到許可的話,那么立即返回false(無(wú)需等待)躏吊。該方法等同于tryAcquire(1, timeout, unit)氛改。
參數(shù):
timeout – 等待許可的最大時(shí)間,負(fù)數(shù)以0處理
unit – 參數(shù)timeout 的時(shí)間單位
返回:
true表示獲取到許可比伏,反之則是false
拋出:
IllegalArgumentException – 如果請(qǐng)求的許可數(shù)為負(fù)數(shù)或者為0
tryAcquire
public boolean tryAcquire(int permits)
從RateLimiter 獲取許可數(shù)胜卤,如果該許可數(shù)可以在無(wú)延遲下的情況下立即獲取得到的話。該方法等同于tryAcquire(permits, 0, anyUnit)赁项。
參數(shù):
permits – 需要獲取的許可數(shù)
返回:
true表示獲取到許可葛躏,反之則是false
拋出:
IllegalArgumentException – 如果請(qǐng)求的許可數(shù)為負(fù)數(shù)或者為0
tryAcquire
public boolean tryAcquire()
從RateLimiter 獲取許可,如果該許可可以在無(wú)延遲下的情況下立即獲取得到的話悠菜。
該方法等同于tryAcquire(1)舰攒。
返回:
true表示獲取到許可,反之則是false
tryAcquire
public boolean tryAcquire(int permits,long timeout,TimeUnit unit)
從RateLimiter 獲取指定許可數(shù)如果該許可數(shù)可以在不超過(guò)timeout的時(shí)間內(nèi)獲取得到的話悔醋,或者如果無(wú)法在timeout 過(guò)期之前獲取得到許可數(shù)的話摩窃,那么立即返回false (無(wú)需等待)。
參數(shù):
permits – 需要獲取的許可數(shù)
timeout – 等待許可數(shù)的最大時(shí)間篙顺,負(fù)數(shù)以0處理
unit – 參數(shù)timeout 的時(shí)間單位返回:
true表示獲取到許可偶芍,反之則是false
拋出:
IllegalArgumentException -如果請(qǐng)求的許可數(shù)為負(fù)數(shù)或者為0
toString
public String toString()
以下描述復(fù)制于java.lang.Object類。
返回對(duì)象的字符表現(xiàn)形式德玫。通常來(lái)講匪蟀,toString 方法返回一個(gè)“文本化呈現(xiàn)”對(duì)象的字符串。
結(jié)果應(yīng)該是一個(gè)簡(jiǎn)明但易于讀懂的信息表達(dá)式宰僧。建議所有子類都重寫(xiě)該方法材彪。
toString 方法返回一個(gè)由實(shí)例的類名,字符’@’和以無(wú)符號(hào)十六進(jìn)制表示的對(duì)象的哈希值組成的字符串。換句話說(shuō)段化,該方法返回的字符串等同于:
getClass().getName() + ‘@’ + Integer.toHexString(hashCode())重載:
Object類的toString方法
返回:
對(duì)象的字符表現(xiàn)形式