基於Java分散式環境下限流系統的設計
前提
業務背景
就拿前些天的雙十一的 “搶券活動” 來說,一般是設置整點開始搶的,你想想,淘寶的用戶群體非常大,可以達到億級別,而服務介面每秒能處理的量是有限的,那麼這個時候問題就會出現,
生產環境
1、服務介面所能提供的服務上限(limit)假如是 500次/s
2、使用者請求介面的次數未知,QPS可能達到 800次/s,1000次/s,或者更高
3、當服務介面的訪問頻率超過 500次/s,超過的量將拒絕服務,多出的資訊將會丟失
4、線上環境是多節點部署的,但是調用的是同一個服務介面
於是,為了保證服務的可用性,
什麼是限流?
限流是對系統的出入流量進行控制,防止大流量出入,導致資源不足,系統不穩定。
限流系統是對資源訪問的控制元件,控制主要的兩個功能:限流策略和熔斷策略,對於熔斷策略,不同的系統有不同的熔斷策略訴求,有的系統希望直接拒絕、有的系統希望排隊等待、有的系統希望服務降級、有的系統會定制自己的熔斷策略,
限流演算法
1、限制暫態併發數
Guava RateLimiter 提供了權杖桶演算法實現:平滑突發限流(SmoothBursty)和平滑預熱限流(SmoothWarmingUp)實現。
2、限制某個介面的時間窗最大請求數
即一個時間視窗內的請求數,如想限制某個介面/服務每秒/每分鐘/每天的請求數/調用量。如一些基礎服務會被很多其他系統調用,比如商品詳情頁服務會調用基礎商品服務調用,
LoadingCache
CacheBuilder.newBuilder()
.expireAfterWrite(2, TimeUnit.SECONDS)
.build(new CacheLoader
@Override
public AtomicLong load(Long seconds) throws Exception {
return new AtomicLong(0);
}
});
long limit = 1000;
while(true) {
//得到當前秒
long currentSeconds = System.currentTimeMillis() / 1000;
if(counter.get(currentSeconds).incrementAndGet() > limit) {
System.out.println("限流了:" + currentSeconds);
continue;
}
//業務處理}
}
使用Guava的Cache來存儲計數器,過期時間設置為2秒(保證1秒內的計數器是有的),然後我們獲取當前時間戳記然後取秒數來作為KEY進行計數統計和限流,這種方式也是簡單粗暴,剛才說的場景夠用了。
3、權杖桶
演算法描述:
假如使用者配置的平均發送速率為r,則每隔1/r秒一個權杖被加入到桶中
假設桶中最多可以存放b個權杖。如果權杖到達時權杖桶已經滿了,那麼這個權杖會被丟棄
當流量以速率v進入,從桶中以速率v取權杖,拿到權杖的流量通過,拿不到權杖流量不通過,執行熔斷邏輯
屬性
長期來看,符合流量的速率是受到權杖添加速率的影響,被穩定為:r
因為權杖桶有一定的存儲量,可以抵擋一定的流量突發情況
M是以位元組/秒為單位的最大可能傳輸速率。 M>r
T max = b/(M-r) 承受最大傳輸速率的時間
B max = T max * M 承受最大傳輸速率的時間內傳輸的流量
優點:流量比較平滑,並且可以抵擋一定的流量突發情況
4、Google guava 提供的工具庫中 RateLimiter 類(內部也是採用權杖桶演算法實現)
最快的方式是使用 RateLimit 類,但是這僅限制在單節點,如果是分散式系統,每個節點的 QPS 是一樣的,請求量到服務介面那的話就是 QPS * 節點數 了。所以這種方案在分散式的情況下不適用!
5、基於 Redis 實現,存儲兩個 key,一個用於計時,一個用於計數。請求每調用一次,計數器增加 1,若在計時器時間內計數器未超過閾值,則可以處理任務。
這種能夠很好地解決了分散式環境下多實例所導致的併發問題。因為使用redis設置的計時器和計數器均是全域唯一的,不管多少個節點,它們使用的都是同樣的計時器和計數器,因此可以做到非常精准的流控。
代碼就不公佈了,畢竟涉及公司隱私了。
如果你想學習Java工程化、高性能及分散式、深入淺出。性能調優、Spring,MyBatis,Netty源碼分析和大資料等知識點可以來找我。
而現在我就有一個平臺可以提供給你們學習,讓你在實踐中積累經驗掌握原理。主要方向是JAVA架構師。如果你想拿高薪,想突破瓶頸,想跟別人競爭能取得優勢的,想進BAT但是有擔心面試不過的,可以加我的Java架構進階群:675047716
注:加群要求
1、具有2-5工作經驗的,面對目前流行的技術不知從何下手,需要突破技術瓶頸的可以加。
2、在公司待久了,過得很安逸,但跳槽時面試碰壁。需要在短時間內進修、跳槽拿高薪的可以加。
3、如果沒有工作經驗,但基礎非常扎實,對java工作機制,常用設計思想,常用java開發框架掌握熟練的,可以加。
4、覺得自己很牛B,一般需求都能搞定。但是所學的知識點沒有系統化,很難在技術領域繼續突破的可以加。
5.阿裡Java高級大牛直播講解知識點,分享知識,多年工作經驗的梳理和總結,帶著大家全面、科學地建立自己的技術體系和技術認知!
6.小號加群一律不給過,謝謝。
轉發此文章請帶上原文連結,否則將追究法律責任!
但是這僅限制在單節點,如果是分散式系統,每個節點的 QPS 是一樣的,請求量到服務介面那的話就是 QPS * 節點數 了。所以這種方案在分散式的情況下不適用!5、基於 Redis 實現,存儲兩個 key,一個用於計時,一個用於計數。請求每調用一次,計數器增加 1,若在計時器時間內計數器未超過閾值,則可以處理任務。
這種能夠很好地解決了分散式環境下多實例所導致的併發問題。因為使用redis設置的計時器和計數器均是全域唯一的,不管多少個節點,它們使用的都是同樣的計時器和計數器,因此可以做到非常精准的流控。
代碼就不公佈了,畢竟涉及公司隱私了。
如果你想學習Java工程化、高性能及分散式、深入淺出。性能調優、Spring,MyBatis,Netty源碼分析和大資料等知識點可以來找我。
而現在我就有一個平臺可以提供給你們學習,讓你在實踐中積累經驗掌握原理。主要方向是JAVA架構師。如果你想拿高薪,想突破瓶頸,想跟別人競爭能取得優勢的,想進BAT但是有擔心面試不過的,可以加我的Java架構進階群:675047716
注:加群要求
1、具有2-5工作經驗的,面對目前流行的技術不知從何下手,需要突破技術瓶頸的可以加。
2、在公司待久了,過得很安逸,但跳槽時面試碰壁。需要在短時間內進修、跳槽拿高薪的可以加。
3、如果沒有工作經驗,但基礎非常扎實,對java工作機制,常用設計思想,常用java開發框架掌握熟練的,可以加。
4、覺得自己很牛B,一般需求都能搞定。但是所學的知識點沒有系統化,很難在技術領域繼續突破的可以加。
5.阿裡Java高級大牛直播講解知識點,分享知識,多年工作經驗的梳理和總結,帶著大家全面、科學地建立自己的技術體系和技術認知!
6.小號加群一律不給過,謝謝。
轉發此文章請帶上原文連結,否則將追究法律責任!