本文已收錄至我的個人網(wǎng)站:程序員波特,主要記錄Java相關(guān)技術(shù)系列教程,共享電子書、Java學(xué)習(xí)路線、視頻教程、簡歷模板和面試題等學(xué)習(xí)資源,讓想要學(xué)習(xí)的你,不再迷茫。
什么是流量預(yù)熱
我們都知道在做運動之前先得來幾組拉伸之類的動作,給身體做個熱身,讓我們的身體平滑過渡到后面的劇烈運動中。流量預(yù)熱也是一樣的道理,對限流組件來說,流量預(yù)熱就類似于一種熱身運動,它可以動態(tài)調(diào)整令牌發(fā)放速度,讓流量變化更加平滑。
我們來舉一個例子:某個接口設(shè)定了100個Request每秒的限流標(biāo)準(zhǔn) ,同時使用令牌桶算法做限流。假如當(dāng)前時間窗口內(nèi)都沒有Reques t過來,那么令牌桶中會裝滿100個令牌。如果在下一秒突然涌入100個請求,這些請求會迅速消耗令牌,對服務(wù)的瞬時沖擊會比較大。因此我們需要一種類似“熱身運動”的緩沖機制,根據(jù)桶內(nèi)的令牌數(shù)量動態(tài)控制令牌的發(fā)放速率,讓忙時流量和閑時流量可以互相平滑過渡。
流量預(yù)熱的做法
我們以Guava
中的RateLimiter
為例,看看流量預(yù)熱在RateLimiter
中是如何運作的,我們用下面的狀態(tài)轉(zhuǎn)換圖來展示整個過程:
橫坐標(biāo)是令牌桶的當(dāng)前容量,縱坐標(biāo)是令牌發(fā)放速率,我們先從橫坐標(biāo)來分析
橫坐標(biāo)
下面兩種場景會導(dǎo)致橫坐標(biāo)的變化:
- 閑時流量流量較小或者壓根沒流量的時候,橫坐標(biāo)會逐漸向右移動,表示令牌桶中令牌數(shù)量增多
- 忙時流量當(dāng)訪問流量增大的時候,橫坐標(biāo)向左移動,令牌桶中令牌數(shù)量變少
橫軸有兩個重要的坐標(biāo),一個是最右側(cè)的“令牌桶最大容量”,這個不難理解。還有一個是Half
容量,它是一個關(guān)鍵節(jié)點,會影響令牌發(fā)放速率。
縱坐標(biāo)
縱坐標(biāo)表示令牌的發(fā)放速率,這里有3個標(biāo)線,分別是穩(wěn)定時間間隔,2倍間隔,3倍間隔。
這里間隔的意思就是隔多長時間發(fā)放一個令牌,而所謂穩(wěn)定間隔就是一個基準(zhǔn)時間間隔。假如我們設(shè)置了每秒10個令牌的限流規(guī)則,那么穩(wěn)定間隔也就是1s/10=0.1秒,也就是說每隔0.1秒發(fā)一個令牌。相應(yīng)的,3倍間隔的數(shù)值是用穩(wěn)定間隔乘以系數(shù)3,比如上面這個例子中3倍間隔就是0.3秒。
運作模式
了解了橫坐標(biāo)和縱坐標(biāo)的含義之后,讓我們來試著理解預(yù)熱模型的用例。繼續(xù)沿用上面10r/s的限流設(shè)置,穩(wěn)定間隔=0.1s,3x間隔是0.3s。
我們先考慮閑時到忙時的流量轉(zhuǎn)變,假定當(dāng)前我們處于閑時流量階段,沒幾個訪問請求,這時令牌桶是滿的。接著在下一秒突然涌入了10個請求,這些請求開始消耗令牌桶中的令牌。在初始階段,令牌的放行速度比較慢,在第一個令牌被消耗以后,后面的請求要經(jīng)過3x時間間隔也就是0.3s才會獲取第二塊令牌。隨著令牌桶中令牌數(shù)量被逐漸消耗,當(dāng)令牌存量下降到最大容量一半的時候(Half位置),令牌放行的速率也會提升,以穩(wěn)定間隔0.1s發(fā)放令牌。
反過來也一樣,在流量從忙時轉(zhuǎn)變?yōu)殚e時的過程中,令牌發(fā)放速率是由快到慢逐漸變化。起始階段的令牌放行間隔是0.1s,隨著令牌桶內(nèi)令牌逐漸增多,當(dāng)令牌的存量積累到最大容量的一半后,放行令牌的時間間隔進一步增大為0.3s。
RateLimiter正是通過這種方式來控制令牌發(fā)放的時間間隔,從而使流量的變化更加平滑。
核心代碼
理解了預(yù)熱模型的運作流程之后,我們來看一下具體代碼是如何實現(xiàn)的。
實現(xiàn)流量預(yù)熱的類是SmoothWarmingUp
,它是SmoothRateLimiter
的一個內(nèi)部類,我們重點關(guān)注一個doSetRate
方法,它是計算橫縱坐標(biāo)系關(guān)鍵節(jié)點的方法,先來看一下SmoothRateLimiter
這個父類中定義的方法
//permitsPerSecond表示每秒可以發(fā)放的令牌數(shù)量
@Override
final void doSetRate(double permitsPerSecond, long nowMicros) {
resync(nowMicros);
//計算穩(wěn)定間隔,使用1s除以令牌桶容量
double stableIntervalMicros = SECONDS.toMicros(1L);
this.stableIntervalMicros = stableIntervalMicros;
//調(diào)用SmoothWarmingUp類中重載的doSetRate方法
doSetRate(permitsPerSecond, stableIntervalMicros);
}
父類在這里的作用主要是計算出了穩(wěn)定時間間隔(使用1s/每秒放行數(shù)量的公式來計算得出),然后預(yù)熱時間、三倍間隔等是在子類的doSetRate
方法中實現(xiàn)的。
接下來我們看子類SmoothWarmingUp
中的doSetRate
做了什么文章來源:http://www.zghlxwxcb.cn/news/detail-802694.html
@Override
void doSetRate(double permitsPerSecond, double stableIn tervalMicros) {
double oldMaxPermits = maxPermits;
//maxPermits表示令牌桶內(nèi)最大容量,它由我們設(shè)置的預(yù)熱時間除以穩(wěn)定時間間隔
maxPermits = warmupPeriodMicros / stableIntervalMicros
//這句不用解釋了吧,halfPermits是最大容量的一半halfPermits =maxPermits / 2.0;
// coldIntervalMicros就是我們前面寫到的3倍間隔,通過穩(wěn)定間//穩(wěn)定間隔是0.1,3倍間隔是0.2,那么平均間隔是0.2
double coldIntervalMicros = stableIntervalMicros * 3
//slope的意思是斜率,也就是前面我們圖中預(yù)熱階段中畫出的斜線//它的計算過程就是一個簡單的求斜率公式
slope = (coldIntervalMicros - stableIntervalMicros)
//計算目前令牌桶的令牌個數(shù)
if (oldMaxPermits == Double.POSITIVE_INFINITY) {
//如果令牌桶最大容量是無窮大,則設(shè)置當(dāng)前可用令牌數(shù)為0 //說實話這段邏輯沒什么用
storedPermits = 0.0;
} else {
storedPermits = (oldMaxPermits == 0.0)
? maxPermits//初始化的狀態(tài)是3x間隔
: storedPermits * maxPermits / oldMaxPermits;
}
}
通過上面的兩個函數(shù),RateLimiter
限流器就對maxPermits
和slope
(預(yù)熱期斜率)兩個變量做了初始化配置。我把關(guān)鍵步驟都注釋在了代碼里,大家理解了之后,可以嘗試去閱讀這個類的其他方法,弄清maxPermits
和slope
是如何影響令牌發(fā)放速率的。文章來源地址http://www.zghlxwxcb.cn/news/detail-802694.html
到了這里,關(guān)于Guava RateLimiter預(yù)熱模型的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!