什么是限流和降級
在開發(fā)高并發(fā)系統(tǒng)時击儡,有很多手段來保護系統(tǒng):緩存、降級蝠引、限流阳谍。
當訪問量快速增長、服務可能會出現一些問題(響應超時)螃概,或者會存在非核心服 務影響到核心流程的性能時矫夯, 仍然需要保證服務的可用性,即便是有損服務吊洼。 所以意味著我們在設計服務的時候训貌,需要一些手段或者關鍵數據進行自動降級,或者配置人工降級的開關冒窍。
緩存的目的是提升系統(tǒng)訪問速度和增大系統(tǒng)處理的容量递沪,可以說是抗高并發(fā)流量的銀彈;
降級是當服務出問題或者影響到核心流程的性能則需要暫時屏蔽掉某些功能,等高 峰或者問題解決后再打開;而有些場景并不能用緩存和降級來解決综液,比如秒殺款慨、搶 購;寫服務(評論、下單)谬莹、頻繁的復雜查詢檩奠,因此需要一種手段來限制這些場景 的并發(fā)/請求量
降級
對于高可用服務的設計桩了,有一個很重要的設計,那就是降級埠戳。降級一般有幾種實現 手段井誉,自動降級和人工降級
通過配置降級開關,實現對流程的控制
前置化降級開關整胃, 基于 OpenResty+配置中心實現降級
業(yè)務降級颗圣,在大促的時候,我們會有限保證核心業(yè)務的流程可用屁使,也就是下單 支付在岂。同時,我們也會對核心的支付流程采取一些異步化的方式來提升吞吐量
限流
限流的目的是防止惡意請求流量屋灌、惡意攻擊、或者防止流量超過系統(tǒng)峰值 限流是對資源訪問做控制的一個組件或者功能应狱,那么控制這塊主要有兩個功能:限流策略和熔斷策略共郭,對于熔斷策略,不同的系統(tǒng)有不同的熔斷策略訴求疾呻,有得系統(tǒng)希望直接拒絕服務除嘹、有的系統(tǒng)希望排隊等待、有的系統(tǒng)希望服務降級岸蜗。限流服務 這塊有兩個核心概念:資源和策略資源:被流量控制的對象尉咕,比如接口策略:限流策略由限流算法和可調節(jié)的參數兩部份組成
限流的目的是通過對并發(fā)訪問/請求進行限速或者一個時間窗口內的請求進行限速 來保護系統(tǒng),一旦達到限制速率則可以拒絕服務(定向到錯誤頁或者告知資源沒有 了)璃岳、排隊或等待(秒殺年缎、下單)、降級(返回兜底數據或默認數據或默認數據铃慷,如商品詳情頁庫存默認有貨)
限流和降級
滑動窗口協(xié)議是傳輸層進行流控的一種措施单芜,接收方通過通告發(fā)送方自己的窗口大小,從而控制發(fā)送方的發(fā)送速度犁柜,從而達到防止發(fā)送方發(fā)送速度過快而導致自己被淹沒的目的洲鸠。
簡單解釋下,發(fā)送和接受方都會維護一個數據幀的序列馋缅,這個序列被稱作窗口扒腕。發(fā) 送方的窗口大小由接受方確定,目的在于控制發(fā)送速度萤悴,以免接受方的緩存不夠大瘾腰, 而導致溢出,同時控制流量也可以避免網絡擁塞覆履。下面圖中的 4,5,6 號數據幀已經 被發(fā)送出去居灯,但是未收到關聯(lián)的 ACK祭务,7,8,9 幀則是等待發(fā)送」窒樱可以看出發(fā)送端的 窗口大小為 6义锥,這是由接受端告知的。此時如果發(fā)送端收到 4 號 ACK岩灭,則窗口的左 邊緣向右收縮拌倍,窗口的右邊緣則向右擴展,此時窗口就向前“滑動了”噪径,即數據幀 10 也可以被發(fā)送
什么是限流和降級
在開發(fā)高并發(fā)系統(tǒng)時柱恤,有很多手段來保護系統(tǒng):緩存、降級找爱、限流梗顺。
當訪問量快速增長、服務可能會出現一些問題(響應超時)车摄,或者會存在非核心服 務影響到核心流程的性能時寺谤, 仍然需要保證服務的可用性,即便是有損服務吮播。 所以意味著我們在設計服務的時候变屁,需要一些手段或者關鍵數據進行自動降級,或者配置人工降級的開關意狠。
緩存的目的是提升系統(tǒng)訪問速度和增大系統(tǒng)處理的容量粟关,可以說是抗高并發(fā)流量的銀彈;
降級是當服務出問題或者影響到核心流程的性能則需要暫時屏蔽掉某些功能,等高 峰或者問題解決后再打開;而有些場景并不能用緩存和降級來解決环戈,比如秒殺闷板、搶 購;寫服務(評論、下單)院塞、頻繁的復雜查詢蛔垢,因此需要一種手段來限制這些場景 的并發(fā)/請求量
降級
對于高可用服務的設計,有一個很重要的設計迫悠,那就是降級鹏漆。降級一般有幾種實現 手段,自動降級和人工降級
通過配置降級開關创泄,實現對流程的控制
前置化降級開關艺玲, 基于 OpenResty+配置中心實現降級
業(yè)務降級,在大促的時候鞠抑,我們會有限保證核心業(yè)務的流程可用饭聚,也就是下單 支付。同時搁拙,我們也會對核心的支付流程采取一些異步化的方式來提升吞吐量
限流
限流的目的是防止惡意請求流量秒梳、惡意攻擊法绵、或者防止流量超過系統(tǒng)峰值 限流是對資源訪問做控制的一個組件或者功能,那么控制這塊主要有兩個功能:限流策略和熔斷策略酪碘,對于熔斷策略朋譬,不同的系統(tǒng)有不同的熔斷策略訴求,有得系統(tǒng)希望直接拒絕服務兴垦、有的系統(tǒng)希望排隊等待徙赢、有的系統(tǒng)希望服務降級。限流服務 這塊有兩個核心概念:資源和策略資源:被流量控制的對象探越,比如接口策略:限流策略由限流算法和可調節(jié)的參數兩部份組成
限流的目的是通過對并發(fā)訪問/請求進行限速或者一個時間窗口內的請求進行限速 來保護系統(tǒng)狡赐,一旦達到限制速率則可以拒絕服務(定向到錯誤頁或者告知資源沒有 了)、排隊或等待(秒殺钦幔、下單)枕屉、降級(返回兜底數據或默認數據或默認數據,如商品詳情頁庫存默認有貨)
限流和降級
滑動窗口協(xié)議是傳輸層進行流控的一種措施鲤氢,接收方通過通告發(fā)送方自己的窗口大小搀擂,從而控制發(fā)送方的發(fā)送速度,從而達到防止發(fā)送方發(fā)送速度過快而導致自己被淹沒的目的铜异。
簡單解釋下哥倔,發(fā)送和接受方都會維護一個數據幀的序列秸架,這個序列被稱作窗口揍庄。發(fā) 送方的窗口大小由接受方確定,目的在于控制發(fā)送速度东抹,以免接受方的緩存不夠大蚂子, 而導致溢出,同時控制流量也可以避免網絡擁塞缭黔。下面圖中的 4,5,6 號數據幀已經 被發(fā)送出去食茎,但是未收到關聯(lián)的 ACK,7,8,9 幀則是等待發(fā)送馏谨”鹩妫可以看出發(fā)送端的 窗口大小為 6,這是由接受端告知的惧互。此時如果發(fā)送端收到 4 號 ACK哎媚,則窗口的左 邊緣向右收縮,窗口的右邊緣則向右擴展喊儡,此時窗口就向前“滑動了”拨与,即數據幀 10 也可以被發(fā)送
漏桶
桶本身具有一個恒定的速率往下漏水,而上方時快時慢的會有水進入桶內艾猜。當桶還 未滿時买喧,上方的水可以加入捻悯。一旦水滿,上方的水就無法加入淤毛。桶滿正是算法中的 一個關鍵的觸發(fā)條件(即流量異常判斷成立的條件)今缚。而此條件下如何處理上方流 下來的水,有兩種方式:
暫時攔截住上方水的向下流動钱床,等待桶中的一部分水漏走后荚斯,再放行上方水。
溢出的上方水直接拋棄 特點
漏水的速率是固定的
即使存在注水 burst(突然注水量變大)的情況查牌,漏水的速率也是固定的
令牌桶(能夠解決突發(fā)流量)
令牌桶算法是網絡流量整形(Traffic Shaping)和速率限制(Rate Limiting)中最常使用的一種算法事期。典型情況下,令牌桶算法用來控制發(fā)送到網絡上的數據的數目纸颜,并允許突發(fā)數據的發(fā)送兽泣。
令牌桶是一個存放固定容量令牌(token)的桶,按照固定速率往桶里添加令牌; 令牌桶算法實際上由三部分組成:兩個流和一個桶胁孙,分別是令牌流唠倦、數據流和令牌桶
限流算法的實際應用
Guava 的 RateLimiter 實現
在 Guava 中 RateLimiter 的實現有兩種: Bursty 和 WarmUp
bursty
bursty 是基于 token bucket 的算法實現,比如
RateLimiter rateLimiter=RateLimiter.create(permitPerSecond);
//創(chuàng)建一個 bursty 實例
rateLimiter.acquire();
//獲取 1 個 permit涮较,當令牌數量不夠時會阻塞直到獲取為止
WarmingUp
基于 Leaky bucket 算法實現
QPS 是固定的
使用于需要預熱時間的使用場景
//創(chuàng)建一個 SmoothWarmingUp 實例;warmupPeriod 是指預熱的時間
RateLimiter create(double permitsPerSecond, long warmupPeriod, TimeUnit unit)
RateLimiter rateLimiter =RateLimiter.create(permitsPerSecond,warmupPeriod,timeUnit);
rateLimiter.acquire();//獲取 1 個 permit;可能會被阻塞止到獲取到為止
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>27.0-jre</version>
</dependency>
public class Token {
//guava->令牌桶稠鼻、漏桶
//bursty(令牌桶)
RateLimiter rateLimiter=RateLimiter.create(10); //qps是1000
//漏桶
RateLimiter rateLimiter1=RateLimiter.create(1000,10, TimeUnit.MILLISECONDS);
public void doPay(){
if(rateLimiter.tryAcquire()){
System.out.println(Thread.currentThread().getName()+"開始執(zhí)行支付");
}else {
System.out.println("系統(tǒng)繁忙");
}
}
public static void main(String[] args) throws IOException {
Token token=new Token();
CountDownLatch countDownLatch=new CountDownLatch(1);
Random random=new Random(10);
for (int i = 0; i < 20; i++) {
new Thread(()->{
try{
countDownLatch.await();
Thread.sleep(random.nextInt(1000));
token.doPay();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
countDownLatch.countDown();
System.in.read();
}
}
差異化演示
import com.google.common.util.concurrent.RateLimiter;
import java.util.concurrent.TimeUnit;
public class TokenDemo {
private int qps;
private int countOfReq;
private RateLimiter rateLimiter;
public TokenDemo(int qps, int countOfReq) {
this.qps = qps;
this.countOfReq = countOfReq;
}
public TokenDemo processWithTokenBucket() {
rateLimiter = RateLimiter.create(qps);
return this;
}
//warmupPeriod 是指預熱的時間
public TokenDemo processWithLeakyBucket() {
rateLimiter = RateLimiter.create(qps, 00, TimeUnit.MILLISECONDS);
return this;
}
private void processRequest() {
System.out.println("RateLimiter:" + rateLimiter.getClass());
long start = System.currentTimeMillis();
for (int i = 0; i < countOfReq; i++) {
rateLimiter.acquire();
}
long end = System.currentTimeMillis() - start;
System.out.println("處理請求數量:" + countOfReq + "," +
"耗時:" + end + "," +
"qps:" + rateLimiter.getRate() + "," +
"實際 qps:" + Math.ceil(countOfReq / (end / 1000.00)));
}
public void doProcess() throws InterruptedException {
for (int i = 0; i < 20; i = i + 5) {
TimeUnit.SECONDS.sleep(i);
processRequest();
}
}
public static void main(String[] args) throws InterruptedException {
new TokenDemo(50, 100).processWithTokenBucket().doProcess();
new TokenDemo(50, 100).processWithLeakyBucket().doProcess();
}
}
分布式下的限流策略
技術選型
mysql:存儲限流策略的參數等元數據 redis+lua:令牌桶算法實現
具體實現
參考 Redisson 中的令牌桶實現邏輯即可