分布式解決方案之:限流

限流是什么尸红?

限流在日常生活中限流很常見(jiàn),例如去有些景區(qū)玩刹泄,每天售賣(mài)的門(mén)票數(shù)是有限的外里,例如 2000 張,即每天最多只有 2000 個(gè)人能進(jìn)去游玩特石。那在我們工程上限流是什么呢盅蝗?限制的是 「流」,在不同場(chǎng)景下「流」的定義不同姆蘸,可以是每秒請(qǐng)求數(shù)墩莫、每秒事務(wù)處理數(shù)、網(wǎng)絡(luò)流量等等逞敷。通常意義我們說(shuō)的限流指代的是限制到達(dá)系統(tǒng)的并發(fā)請(qǐng)求數(shù)狂秦,使得系統(tǒng)能夠正常的處理部分用戶(hù)的請(qǐng)求,來(lái)保證系統(tǒng)的穩(wěn)定性兰粉。

限流的本質(zhì)是因?yàn)楹蠖颂幚砟芰τ邢薰嗜枰氐舫^(guò)處理能力之外的請(qǐng)求,亦或是為了均衡客戶(hù)端對(duì)服務(wù)端資源的公平調(diào)用玖姑,防止一些客戶(hù)端餓死愕秫。

為什么要限流?

  • 為了保證系統(tǒng)的穩(wěn)定性焰络。

日常的業(yè)務(wù)上有類(lèi)似秒殺活動(dòng)戴甩、雙十一大促或者突發(fā)新聞等場(chǎng)景,用戶(hù)的流量突增闪彼,后端服務(wù)的處理能力是有限的甜孤,如果不能處理好突發(fā)流量协饲,后端服務(wù)很容易就被打垮。另外像爬蟲(chóng)之類(lèi)的不正常流量缴川,我們對(duì)外暴露的服務(wù)都要以最大惡意為前提去防備調(diào)用者茉稠。我們不清楚調(diào)用者會(huì)如何調(diào)用我們的服務(wù),假設(shè)某個(gè)調(diào)用者開(kāi)幾十個(gè)線(xiàn)程一天二十四小時(shí)瘋狂調(diào)用你的服務(wù)把夸,如果不做啥處理咱服務(wù)基本也玩完了而线,更勝者還有ddos攻擊。

  • 保證資源公平利用

對(duì)于很多第三方開(kāi)放平臺(tái)來(lái)說(shuō)恋日,不僅僅要防備不正常流量膀篮,還要保證資源的公平利用,一些接口資源不可能一直都被一個(gè)客戶(hù)端占著岂膳,也需要保證其他客戶(hù)端能正常調(diào)用誓竿。

例子:高德開(kāi)放平臺(tái)流量限制說(shuō)明

圖片

常見(jiàn)的限流算法

1、計(jì)數(shù)器限流

計(jì)數(shù)器限流也就是最簡(jiǎn)單的限流算法就是計(jì)數(shù)限流了谈截。例如系統(tǒng)能同時(shí)處理 100 個(gè)請(qǐng)求筷屡,保存一個(gè)計(jì)數(shù)器,處理了一個(gè)請(qǐng)求簸喂,計(jì)數(shù)器就加一速蕊,一個(gè)請(qǐng)求處理完畢之后計(jì)數(shù)器減一。每次請(qǐng)求來(lái)的時(shí)候看看計(jì)數(shù)器的值娘赴,如果超過(guò)閾值就拒絕。計(jì)數(shù)器的值要是存內(nèi)存中就算單機(jī)限流算法跟啤,如果放在第三方存儲(chǔ)里(例如Redis中)集群機(jī)器訪問(wèn)就算分布式限流算法诽表。

  • 優(yōu)點(diǎn):簡(jiǎn)單,單機(jī)在 Java 中可用 Atomic 等原子類(lèi)隅肥、分布式就使用 Redis incr命令竿奏。

  • 缺點(diǎn):假設(shè)我們?cè)试S的閾值是1萬(wàn),此時(shí)計(jì)數(shù)器的值為 0腥放,當(dāng) 1 萬(wàn)個(gè)請(qǐng)求在1秒內(nèi)全部請(qǐng)求進(jìn)來(lái)泛啸,那么服務(wù)可能就扛不住了。

計(jì)數(shù)器限流偽代碼實(shí)現(xiàn)

boolean tryAcquire() {
    if (counter < threshold)  {
        counter++;
        return true;
    }
    return false;
}

boolean tryRelease() {
    if (counter > 0)  {
        counter--;
        return true;
    }
    return false;
}

一般的限流都是為了限制在指定時(shí)間間隔內(nèi)的訪問(wèn)量秃症,因此還有個(gè)算法叫固定窗口候址。

2、固定窗口限流

它相比于計(jì)數(shù)限流主要是多了個(gè)時(shí)間窗口的概念种柑,計(jì)數(shù)器每過(guò)一個(gè)時(shí)間窗口就重置岗仑。規(guī)則如下:

  • 請(qǐng)求次數(shù)小于閾值,允許訪問(wèn)并且計(jì)數(shù)器 +1聚请;

  • 請(qǐng)求次數(shù)大于閾值荠雕,拒絕訪問(wèn);

  • 這個(gè)時(shí)間窗口過(guò)了之后,計(jì)數(shù)器清零炸卑;

固定窗口限流偽代碼實(shí)現(xiàn)

boolean trvAcquire() {
    // 獲取當(dāng)前時(shí)間
    long now = currentTimeMillis( )既鞠;
    // 看是否過(guò)了時(shí)間窗口
    if (now - lastAcquireTime > TimeWindow ) { 
        // 計(jì)數(shù)器置零
        counter = 0;
        lastAcquireTime = now;
    }
    // 小于閾值
    if (counter < threshold) { 
        counter++;
        return true;
    }
    return false
}

這種方式也會(huì)面臨一些問(wèn)題,例如固定窗口臨界問(wèn)題:假設(shè)系統(tǒng)每秒允許 100 個(gè)請(qǐng)求盖文,假設(shè)第一個(gè)時(shí)間窗口是 0-1s嘱蛋,在第 0.55s 處一下次涌入 100 個(gè)請(qǐng)求,過(guò)了 1 秒的時(shí)間窗口后計(jì)數(shù)清零椅寺,此時(shí)在 1.05 s 的時(shí)候又一下次涌入100個(gè)請(qǐng)求浑槽。雖然窗口內(nèi)的計(jì)數(shù)沒(méi)超過(guò)閾值,但是全局來(lái)看在 0.55s-1.05s 這 0.1 秒內(nèi)涌入了 200 個(gè)請(qǐng)求返帕,這其實(shí)對(duì)于閾值是 100/s 的系統(tǒng)來(lái)說(shuō)是無(wú)法接受的桐玻。

image-20210420115824978

為了解決這個(gè)問(wèn)題,業(yè)界又提出另外一種限流算法荆萤,即滑動(dòng)窗口限流镊靴。

3冷冗、滑動(dòng)窗口限流

滑動(dòng)窗口限流解決固定窗口臨界值的問(wèn)題言津,可以保證在任意時(shí)間窗口內(nèi)都不會(huì)超過(guò)閾值。相對(duì)于固定窗口民轴,滑動(dòng)窗口除了需要引入計(jì)數(shù)器之外還需要記錄時(shí)間窗口內(nèi)每個(gè)請(qǐng)求到達(dá)的時(shí)間點(diǎn)敞峭,因此對(duì)內(nèi)存的占用會(huì)比較多踊谋。

規(guī)則如下,假設(shè)時(shí)間窗口為 1 秒:

  • 記錄每次請(qǐng)求的時(shí)間

  • 統(tǒng)計(jì)每次請(qǐng)求的時(shí)間至往前推1秒這個(gè)時(shí)間窗口內(nèi)的請(qǐng)求數(shù)旋讹,并且1 秒前的數(shù)據(jù)可以清除殖蚕。

  • 統(tǒng)計(jì)的請(qǐng)求數(shù)小于閾值就記錄這個(gè)請(qǐng)求的時(shí)間,并允許通過(guò)沉迹,反之拒絕睦疫。

滑動(dòng)窗口

圖片

滑動(dòng)窗口偽代碼實(shí)現(xiàn)

boolean tryAcquire() {
    long now = currentTimenillis(); 
    // 根據(jù)當(dāng)前時(shí)間獲取時(shí)間窗口內(nèi)的計(jì)數(shù)
    long counter = getCounterInTimewindow(now);

    // 小于閾值
    if (counter < threshold){ 
        // 記錄當(dāng)前時(shí)間
        addToTimewindow(now);
        return true;
    }
    return false;

但是滑動(dòng)窗口和固定窗口都無(wú)法解決短時(shí)間之內(nèi)集中流量的沖擊問(wèn)題鞭呕。我們所想的限流場(chǎng)景是: 每秒限制 100 個(gè)請(qǐng)求蛤育。希望請(qǐng)求每 10ms 來(lái)一個(gè),這樣我們的流量處理就很平滑葫松,但是真實(shí)場(chǎng)景很難控制請(qǐng)求的頻率瓦糕,因?yàn)榭赡芫退阄覀冊(cè)O(shè)置了1s內(nèi)只能有100個(gè)請(qǐng)求,也可能存在 5ms 內(nèi)就打滿(mǎn)了閾值的情況进宝。當(dāng)然對(duì)于這種情況還是有變型處理的刻坊,例如設(shè)置多條限流規(guī)則。不僅限制每秒 100 個(gè)請(qǐng)求党晋,再設(shè)置每 10ms 不超過(guò) 2 個(gè)谭胚,不過(guò)帶來(lái)的就是比較差的用戶(hù)體驗(yàn)徐块。

而漏桶算法,可以解決時(shí)間窗口類(lèi)的痛點(diǎn)灾而,使得流量更加平滑胡控。

4、漏桶算法

如下圖所示旁趟,水滴持續(xù)滴入漏桶中昼激,底部定速流出。如果水滴滴入的速率大于流出的速率锡搜,當(dāng)存水超過(guò)桶的大小的時(shí)候就會(huì)溢出橙困。

規(guī)則如下:

  • 請(qǐng)求來(lái)了放入桶中(不限制流入速率)

  • 桶內(nèi)請(qǐng)求量滿(mǎn)了拒絕請(qǐng)求

  • 服務(wù)定速?gòu)耐皟?nèi)拿請(qǐng)求處理(定速流出)

漏桶算法示意圖

圖片

漏桶偽代碼實(shí)現(xiàn)

LeakyDemo {
  public long timeStamp = getNowTime();
  public int capacity; // 桶的容量
  public int rate; // 水漏出的速度
  public Long water; // 當(dāng)前水量(當(dāng)前累積請(qǐng)求數(shù))
  public boolean grant() {
    long now = getNowTime();
    water = Math.max(0L, water - (now - timeStamp) * rate); // 先執(zhí)行漏水,計(jì)算剩余水量
    timeStamp = now;
    if ((water + 1) < capacity) {
      // 嘗試加水,并且水還未滿(mǎn)
      water += 1;
      return true;
    }
    else {
    // 水滿(mǎn)耕餐,拒絕加水
    return false;
    }
 }
 private static Long getNowTime(){
   return System.currentTimeMillis();
 }
}

水滴對(duì)應(yīng)的就是請(qǐng)求凡傅。

  • 特點(diǎn):流量寬進(jìn)嚴(yán)出。無(wú)論請(qǐng)求多少肠缔,請(qǐng)求的速率有多大夏跷,都按照固定的速率流出,對(duì)應(yīng)的就是服務(wù)按照固定的速率處理請(qǐng)求明未。和消息隊(duì)列思想有點(diǎn)像槽华,削峰填谷。一般而言漏桶也是由隊(duì)列來(lái)實(shí)現(xiàn)的趟妥,處理不過(guò)來(lái)的請(qǐng)求就排隊(duì)猫态,隊(duì)列滿(mǎn)了就拒絕請(qǐng)求。

與線(xiàn)程池實(shí)現(xiàn)的方式方式如出一轍披摄。

  • 缺點(diǎn)

面對(duì)突發(fā)請(qǐng)求懂鸵,服務(wù)的處理速度和平時(shí)是一樣的,這并非我們實(shí)際想要的行疏。我們希望的是在突發(fā)流量時(shí),在保證系統(tǒng)平穩(wěn)的同時(shí)套像,也要盡可能提升用戶(hù)體驗(yàn)酿联,也就是能更快地處理并響應(yīng)請(qǐng)求,而不是和正常流量一樣循規(guī)蹈矩地處理夺巩。

而令牌桶在應(yīng)對(duì)突擊流量的時(shí)候贞让,可以更加的“激進(jìn)”。

5柳譬、令牌桶算法

令牌桶其實(shí)和漏桶的原理類(lèi)似喳张,只不過(guò)漏桶是定速地流出,而令牌桶是定速地往桶里塞入令牌美澳,然后請(qǐng)求只有拿到了令牌才能通過(guò)销部,之后再被服務(wù)器處理摸航。

當(dāng)然令牌桶的大小也是有限制的,假設(shè)桶里的令牌滿(mǎn)了之后舅桩,定速生成的令牌會(huì)丟棄酱虎。

規(guī)則:

  • 定速的往桶內(nèi)放入令牌(定速生成)

  • 令牌數(shù)量超過(guò)桶的限制,丟棄

  • 請(qǐng)求來(lái)了先向桶內(nèi)索要令牌擂涛,索要成功則通過(guò)被處理读串,反之拒絕

令牌桶

圖片

令牌桶的原理與JUC的Semaphore 信號(hào)量很相似,信號(hào)量可控制某個(gè)資源被同時(shí)訪問(wèn)的個(gè)數(shù)撒妈,其實(shí)和拿令牌思想一樣恢暖,不同的是一個(gè)是拿信號(hào)量,一個(gè)是拿令牌狰右。信號(hào)量用完了返還杰捂,而令牌用了不歸還,因?yàn)榱钆茣?huì)定時(shí)再填充挟阻。

借助Semaphore實(shí)現(xiàn)單機(jī)流量限制(其原理與令牌桶算法基本一致)

import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;

/**
 * @author: xiebochang
 * @Title: SemaphoreLocalRateLimiter
 * @date: 2021/4/20
 */
public class SemaphoreLocalRateLimiter {

    public static void main (String[] args) {
        ExecutorService service = Executors.newFixedThreadPool(10);
        // 最多允許5個(gè)線(xiàn)程同時(shí)訪問(wèn)
        int limit = 5;
        Semaphore semaphore = new Semaphore(limit);
        // 模擬10個(gè)請(qǐng)求同時(shí)涌進(jìn)來(lái)
        int requiredCount = 10;
        for (int i = 0; i < requiredCount; i++) {
        final int num = i;
        service.submit(() -> {
            try {
                // 獲取許可
                semaphore.acquire();
                // 每個(gè)線(xiàn)程睡[0,10)s后釋放許可
                System.out.println(num + "獲得許可琼娘,開(kāi)始執(zhí)行");
                long sleepTime = new Random().nextInt(10000);
                Thread.sleep(sleepTime);
                semaphore.release();
                System.out.println(num + "釋放許可," + "休眠了" + sleepTime + "ms");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
 }
   service.shutdown();
 /**
 *
 * 執(zhí)行結(jié)果示例:(在5個(gè)許可發(fā)放完畢后附鸽,只有等拿到許可的線(xiàn)程釋放了許可脱拼,其它未獲取許可的線(xiàn)程才能獲取許可,執(zhí)行業(yè)務(wù)代碼)
 *
 * 5獲得許可坷备,開(kāi)始執(zhí)行
 * 0獲得許可熄浓,開(kāi)始執(zhí)行
 * 4獲得許可,開(kāi)始執(zhí)行
 * 2獲得許可省撑,開(kāi)始執(zhí)行
 * 3獲得許可赌蔑,開(kāi)始執(zhí)行
 * 3釋放許可,休眠了964ms
 * 1獲得許可竟秫,開(kāi)始執(zhí)行
 * 0釋放許可娃惯,休眠了5139ms
 * 6獲得許可,開(kāi)始執(zhí)行
 * 5釋放許可肥败,休眠了6105ms
 * 7獲得許可趾浅,開(kāi)始執(zhí)行
 * 7釋放許可,休眠了319ms
 * 8獲得許可馒稍,開(kāi)始執(zhí)行
 * 4釋放許可皿哨,休眠了6912ms
 * 9獲得許可,開(kāi)始執(zhí)行
 * 2釋放許可纽谒,休眠了8870ms
 * 8釋放許可证膨,休眠了3079ms
 * 1釋放許可,休眠了9480ms
 * 6釋放許可鼓黔,休眠了6018ms
 * Disconnected from the target VM, address: '127.0.0.1:64441', transport: 'socket'
 * 9釋放許可央勒,休眠了7000ms
 *
 **/
 }

}

Guava包RateLimiter實(shí)現(xiàn)簡(jiǎn)單限流

/**
 * 簡(jiǎn)單的tryAcquire
 *
 * @param
 * @return void
 **/
 private static void testSimpleTryAcquire () throws InterruptedException {
    RateLimiter limiter = RateLimiter.create(2);
    while (true) {
    Thread.sleep(2000);
    System.out.println("tryAcquire:" + limiter.tryAcquire(2));
 }
 /**
 *
 * output:
 *
 * tryAcquire:true
 * tryAcquire:true
 * tryAcquire:true
 *
 **/
 }

 /**
 *
 * 默認(rèn)使用SmoothBursty -- 平滑突發(fā)限流
 * 一開(kāi)始便初始化填入permitsPerSecond個(gè)令牌
 * 不預(yù)熱不见,一開(kāi)始就固定令牌生成速率
 *
 * @param permitsPerSecond -- 每秒生成token數(shù)
 * @param requiredPermits -- 請(qǐng)求token數(shù)
 * @return void
 **/
 private static void simpleSmoothBursty (double permitsPerSecond, int requiredPermits) {
    RateLimiter limiter = RateLimiter.create(permitsPerSecond);
    int i = 1;
    while (true) {
    System.out.println("round " + i + ":get " + requiredPermits + " tokens spend:" + limiter.acquire(requiredPermits) + "s");
    i ++;
 }
 /**
 * permitsPerSecond = 2,requiredPermits = 2
 *
 * output:
 *
 * round 1:get 2 tokens spend:0.0s
 * round 2:get 2 tokens spend:0.997221s
 * round 3:get 2 tokens spend:0.99515s
 * round 4:get 2 tokens spend:0.992469s
 * round 5:get 2 tokens spend:0.996439s
 * round 6:get 2 tokens spend:0.999554s
 *
 **/
 }

簡(jiǎn)單令牌桶算法偽代碼

/**
 * @author: xiebochang
 * @Title: LocalRateLimiter
 * @date: 2021/4/20
 */
public class LocalRateLimiter {

    private long tokens = getInitTokens();
    private long lastRefreshTime = getInitLastRefreshTime();
    private int rate = 1; // 1ms生成1個(gè)

    public boolean tryGrant (long required, long capacity) {
        long now = System.currentTimeMillis();
        // 計(jì)算生成令牌數(shù)
        long generateTokens = Math.max(0, (now - lastRefreshTime) * rate);
        // 可取令牌數(shù)
        long currTokens = Math.min(tokens + generateTokens, capacity);
        // 是否足夠取
        boolean allowed = currTokens >= required;
        if (allowed) {
            tokens = currTokens - required;
            lastRefreshTime = now;
            return true;
        }
        return false;
     }

    private long getInitTokens() {
      return 100;
    }

    private long getInitLastRefreshTime() {
        return lastRefreshTime > 0 ? lastRefreshTime : System.currentTimeMillis() - 1000;
    }

    public static void main (String[] args) throws InterruptedException {
        LocalRateLimiter limiter = new LocalRateLimiter();
        System.out.println(limiter.tryGrant(1200, 3000));
        Thread.sleep(1000);
        System.out.println(limiter.tryGrant(2100, 3000));
    }
}

對(duì)比漏桶算法可以看出令牌桶更適合應(yīng)對(duì)突發(fā)流量订歪,假如桶內(nèi)有 100 個(gè)令牌脖祈,那么這100個(gè)令牌可以馬上被取走,而不像漏桶那樣勻速的消費(fèi)刷晋。不過(guò)上面批量獲取令牌也會(huì)致使一些新的問(wèn)題出現(xiàn)盖高,比如導(dǎo)致一定范圍內(nèi)的限流誤差,舉個(gè)例子你取了 10 個(gè)此時(shí)不用眼虱,等下一秒再用喻奥,那同一時(shí)刻集群機(jī)器總處理量可能會(huì)超過(guò)閾值,所以現(xiàn)實(shí)中使用時(shí)捏悬,可能不會(huì)去考慮redis頻繁讀取問(wèn)題撞蚕,轉(zhuǎn)而直接采用一次獲取一個(gè)令牌的方式,具體采用哪種策略還是要根據(jù)真實(shí)場(chǎng)景而定过牙。

限流算法總結(jié)

1甥厦、計(jì)數(shù)器 VS 固定窗口 VS 滑動(dòng)窗口

計(jì)數(shù)器可以說(shuō)是固定窗口的低精度實(shí)現(xiàn),固定窗口又可以說(shuō)是滑動(dòng)窗口的低精度實(shí)現(xiàn)寇钉。

因此在限流時(shí)間精度上刀疙,三種算法關(guān)系為:計(jì)數(shù)器<固定窗口<滑動(dòng)窗口

但精度的精確同時(shí)也要消耗內(nèi)存,因此三種算法內(nèi)存占用關(guān)系為:計(jì)數(shù)器<固定窗口<滑動(dòng)窗口

2扫倡、漏桶算法 VS 令牌桶算法

兩者都比較適合阻塞式的限流場(chǎng)景谦秧。(類(lèi)似分布式鎖,獲取不到令牌則一直等待)

漏桶算法是將請(qǐng)求暫存于桶內(nèi)撵溃,再定速處理疚鲤,不大符合互聯(lián)網(wǎng)業(yè)務(wù)中低延遲的需求,用戶(hù)體驗(yàn)不佳缘挑。而令牌桶算法在限流時(shí)集歇,還可應(yīng)對(duì)突增流量的場(chǎng)景(可以批量獲取令牌),對(duì)用戶(hù)端的體驗(yàn)較好语淘。

但令牌桶算法使用不當(dāng)也有弊端鬼悠,比如上線(xiàn)初期未對(duì)桶內(nèi)的令牌做初始化,在第一批令牌還未生成時(shí)接受到請(qǐng)求會(huì)被丟棄亏娜,造成誤殺。

總的來(lái)說(shuō)

  • 計(jì)數(shù)器比較適合簡(jiǎn)單蹬挺、粗力度限流需求

  • 固定/滑動(dòng)窗口則適合對(duì)響應(yīng)時(shí)間要求較高的限流場(chǎng)景维贺,比如一些微服務(wù)接口

  • 漏桶算法則比較適合后臺(tái)任務(wù)類(lèi)的限流場(chǎng)景

  • 令牌桶則適用于大流量接口,特別是有瞬時(shí)流量較高的接口限流場(chǎng)景巴帮。

單機(jī)限流和分布式限流

單機(jī)限流和分布式限流本質(zhì)上的區(qū)別在于 “閾值” 存放的位置溯泣,單機(jī)限流就是“閥值”存放在單機(jī)部署的服務(wù)/內(nèi)存中虐秋,但我們的服務(wù)往往是集群部署的,因此需要多臺(tái)機(jī)器協(xié)同提供限流功能垃沦。像上述的計(jì)數(shù)器或者時(shí)間窗口的算法客给,可以將計(jì)數(shù)器存放至 Redis 等分布式 K-V 存儲(chǔ)中。又如滑動(dòng)窗口的每個(gè)請(qǐng)求的時(shí)間記錄可以利用 Redis 的 zset 存儲(chǔ)肢簿,利用ZREMRANGEBYSCORE 刪除時(shí)間窗口之外的數(shù)據(jù)靶剑,再用 ZCARD計(jì)數(shù),

限流的難點(diǎn)

  • 如何設(shè)定閥值池充?

可以看到桩引,每個(gè)限流都有個(gè)閾值,這個(gè)閾值如何定是個(gè)難點(diǎn)收夸。定大了服務(wù)器可能頂不住坑匠,定小了就“誤殺”了,沒(méi)有資源利用最大化卧惜,對(duì)用戶(hù)體驗(yàn)不好厘灼。一般的做法是限流上線(xiàn)之后先預(yù)估個(gè)大概的閾值,然后不執(zhí)行真正的限流操作咽瓷,而是采取日志記錄方式设凹,對(duì)日志進(jìn)行分析查看限流的效果,然后調(diào)整閾值忱详,推算出集群總的處理能力围来,和每臺(tái)機(jī)子的處理能力(方便擴(kuò)縮容)。然后將線(xiàn)上的流量進(jìn)行重放匈睁,測(cè)試真正的限流效果监透,最終閾值確定,然后上線(xiàn)航唆。

其實(shí)真實(shí)的業(yè)務(wù)場(chǎng)景很復(fù)雜胀蛮,需要限流的條件和資源很多,每個(gè)資源限流要求還不一樣糯钙。

限流組件

一般而言粪狼,我們不需要自己實(shí)現(xiàn)限流算法來(lái)達(dá)到限流的目的,不管是接入層限流還是細(xì)粒度的接口限流任岸,都有現(xiàn)成的輪子使用再榄,其實(shí)現(xiàn)也是用了上述我們所說(shuō)的限流算法。

  • Google Guava 提供的限流工具類(lèi) RateLimiter享潜,是基于令牌桶實(shí)現(xiàn)的困鸥,并且擴(kuò)展了算法,支持預(yù)熱功能剑按。

  • 阿里開(kāi)源的限流框架Sentinel 中的勻速排隊(duì)限流策略疾就,就采用了漏桶算法澜术。

  • Nginx 中的限流模塊 limit_req_zone,采用了漏桶算法猬腰,還有 OpenResty 中的 resty.limit.req庫(kù)等等鸟废。

  • 基于redis lua腳本 / redisson實(shí)現(xiàn)的令牌桶。

具體的使用還是很簡(jiǎn)單的姑荷,有興趣的同學(xué)可以自行搜索盒延,對(duì)內(nèi)部實(shí)現(xiàn)感興趣的同學(xué)可以下個(gè)源碼看看,學(xué)習(xí)下生產(chǎn)級(jí)別的限流是如何實(shí)現(xiàn)的厢拭。

總結(jié)

限流具體應(yīng)用到工程還是有很多點(diǎn)需要考慮的兰英,并且限流只是保證系統(tǒng)穩(wěn)定性中的一個(gè)環(huán)節(jié),還需要配合降級(jí)供鸠、熔斷等相關(guān)內(nèi)容畦贸。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市楞捂,隨后出現(xiàn)的幾起案子薄坏,更是在濱河造成了極大的恐慌,老刑警劉巖寨闹,帶你破解...
    沈念sama閱讀 212,222評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件胶坠,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡繁堡,警方通過(guò)查閱死者的電腦和手機(jī)沈善,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,455評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)椭蹄,“玉大人闻牡,你說(shuō)我怎么就攤上這事∩兀” “怎么了罩润?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,720評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)翼馆。 經(jīng)常有香客問(wèn)我割以,道長(zhǎng),這世上最難降的妖魔是什么应媚? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,568評(píng)論 1 284
  • 正文 為了忘掉前任严沥,我火速辦了婚禮,結(jié)果婚禮上中姜,老公的妹妹穿的比我還像新娘消玄。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,696評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布莱找。 她就那樣靜靜地躺著,像睡著了一般嗜桌。 火紅的嫁衣襯著肌膚如雪奥溺。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,879評(píng)論 1 290
  • 那天骨宠,我揣著相機(jī)與錄音浮定,去河邊找鬼。 笑死层亿,一個(gè)胖子當(dāng)著我的面吹牛桦卒,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播匿又,決...
    沈念sama閱讀 39,028評(píng)論 3 409
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼方灾,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了碌更?” 一聲冷哼從身側(cè)響起裕偿,我...
    開(kāi)封第一講書(shū)人閱讀 37,773評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎痛单,沒(méi)想到半個(gè)月后嘿棘,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,220評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡旭绒,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,550評(píng)論 2 327
  • 正文 我和宋清朗相戀三年鸟妙,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片挥吵。...
    茶點(diǎn)故事閱讀 38,697評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡重父,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蔫劣,到底是詐尸還是另有隱情坪郭,我是刑警寧澤,帶...
    沈念sama閱讀 34,360評(píng)論 4 332
  • 正文 年R本政府宣布脉幢,位于F島的核電站歪沃,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏嫌松。R本人自食惡果不足惜沪曙,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,002評(píng)論 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望萎羔。 院中可真熱鬧液走,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,782評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至巷懈,卻和暖如春该抒,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背顶燕。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,010評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工凑保, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人涌攻。 一個(gè)月前我還...
    沈念sama閱讀 46,433評(píng)論 2 360
  • 正文 我出身青樓欧引,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親恳谎。 傳聞我的和親對(duì)象是個(gè)殘疾皇子芝此,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,587評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容