亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Java代碼實(shí)現(xiàn)四種限流算法詳細(xì)介紹

 更新時(shí)間:2024年05月20日 10:57:27   作者:Java雪荷  
本文主要介紹了Java代碼實(shí)現(xiàn)四種限流算法詳細(xì)介紹,包含固定窗口限流,滑動(dòng)窗口限流,漏桶限流,令牌桶限流,具有一定的參考價(jià)值,感興趣的可以了解一下

前言

上個(gè)月做了一個(gè)BI項(xiàng)目并且使用了限流算法,目的是為了限制用戶瘋狂調(diào)用AI生成接口造成財(cái)產(chǎn)損失,畢竟AI的調(diào)用是要馬內(nèi)的。這個(gè)篇文章會(huì)仔細(xì)探討介紹四種常見(jiàn)的限流算法及其Java代碼實(shí)現(xiàn)。

固定窗口限流

將單位時(shí)間內(nèi)作為一個(gè)時(shí)間窗口,同時(shí)維護(hù)一個(gè)計(jì)數(shù)器,記錄次時(shí)間窗口內(nèi)接收的請(qǐng)求次數(shù)。

  • 當(dāng)次數(shù)小于等于限流閾值,允許請(qǐng)求通過(guò),并且計(jì)數(shù)器 +1
  • 當(dāng)次數(shù)大于限流閾值,不允許請(qǐng)求通過(guò),計(jì)數(shù)器不變
  • 當(dāng)系統(tǒng)時(shí)間(當(dāng)前時(shí)間)超過(guò)時(shí)間窗口,計(jì)數(shù)器重置為0并且開(kāi)始記錄新的窗口內(nèi)接收的請(qǐng)求次數(shù)

缺陷:定義時(shí)間窗口為1s,允許請(qǐng)求數(shù)量為3(請(qǐng)求閾值),說(shuō)明1s內(nèi)只能接收并處理3個(gè)請(qǐng)求。然而如果0.8~0.9s來(lái)了三個(gè)請(qǐng)求,1.1~1.2s來(lái)了三個(gè)請(qǐng)求。雖然這6個(gè)請(qǐng)求都沒(méi)超過(guò)各自窗口的請(qǐng)求閾值,但是已經(jīng)違背了1s內(nèi)接收并處理3個(gè)請(qǐng)求了,因?yàn)樵诓坏?s內(nèi)其接收了6個(gè)請(qǐng)求,存在臨界問(wèn)題。

優(yōu)點(diǎn):簡(jiǎn)單易實(shí)現(xiàn)

缺點(diǎn):

  • 有臨界問(wèn)題(流量突刺)
  • 不適用于分布式系統(tǒng)

Java代碼實(shí)現(xiàn)

public class FixedWindowRateLimiter {
    private final int requestLimit; // 限流閾值
    private final long windowSize; // 時(shí)間窗口大?。ê撩耄?
    private int count; // 計(jì)數(shù)器
    private volatile long windowStart; // 時(shí)間窗口起始時(shí)間

    public FixedWindowRateLimiter(long windowSize, int requestLimit) {
        this.windowSize = windowSize;
        this.requestLimit = requestLimit;
        this.count = 0;
        this.windowStart = System.currentTimeMillis() / 1000;
    }

    public synchronized boolean tryAcquire() {
        long currentTime = System.currentTimeMillis();
        if (currentTime - windowStart > windowSize) { // 如果當(dāng)前時(shí)間已經(jīng)超出窗口時(shí)間,則重置窗口
            windowStart = currentTime;
            count = 0;
        }
        if (count < requestLimit) {
            count++; // 增加計(jì)數(shù)
            return true;
        } else {
            return false;
        }
    }

    public static void main(String[] args) {
        FixedWindowRateLimiter fixedWindowRateLimiter = new FixedWindowRateLimiter(10, 2); // 時(shí)間窗口大小,請(qǐng)求閾值(請(qǐng)求限制)
        for (int i = 0;i < 10;i++) {
            boolean allowed = fixedWindowRateLimiter.tryAcquire();
            System.out.println("請(qǐng)求" + (i+1) + ":" + (allowed ? "被允許" : "被拒絕"));
        }
    }
}

結(jié)果:

滑動(dòng)窗口限流

為了解決固定窗口限流的臨界問(wèn)題,便有了滑動(dòng)窗口限流算法。

滑動(dòng)窗口將單位時(shí)間劃分n個(gè)小周期,通過(guò)計(jì)數(shù)器記錄每個(gè)小周期內(nèi)的請(qǐng)求次數(shù),每過(guò)0.2s后時(shí)間窗口就往后推移1小格即一個(gè)小周期(0.2s),那么最初的小周期就會(huì)被刪除,并且隨著時(shí)間的推移刪除已過(guò)期的小周期。

比如,將單位時(shí)間1s劃分5個(gè)小周期,每個(gè)小周期為0.2s。每個(gè)小周期內(nèi)的請(qǐng)求次數(shù)由各自且獨(dú)立的計(jì)數(shù)器記錄,統(tǒng)計(jì)5個(gè)小周期的計(jì)數(shù)器總和是否超過(guò)請(qǐng)求閾值,沒(méi)有就請(qǐng)求允許,否則請(qǐng)求被拒絕。起初滑動(dòng)窗口是0~1s;當(dāng)過(guò)了0.2s后,0~0.2s這個(gè)小周期就會(huì)被刪除,同時(shí)滑動(dòng)窗口后移0.2s(一個(gè)小周期),那么新的滑動(dòng)窗口變?yōu)榱?.2~1.2s。

用上述固定窗口的臨界例子應(yīng)用到滑動(dòng)窗口:0.8~0.9來(lái)了三個(gè)請(qǐng)求,這三個(gè)請(qǐng)求被允許了(此時(shí)滑動(dòng)窗口是0~1.0s)。過(guò)了0.2s后,新的滑動(dòng)窗口變?yōu)榱?.2~1.2s,當(dāng)1.1~1.2s來(lái)了三個(gè)請(qǐng)求,這三個(gè)請(qǐng)求就會(huì)被拒絕。因?yàn)榇藭r(shí)新的5個(gè)小周期的計(jì)數(shù)器總和為3達(dá)到了請(qǐng)求閾值,所以.1~1.2s來(lái)的三個(gè)請(qǐng)求會(huì)被拒絕。

優(yōu)點(diǎn):解決了固定限流算法的臨界(流量突刺)問(wèn)題

缺點(diǎn):

  • 相比固定限流難理解和實(shí)現(xiàn)
  • 不適用分布式系統(tǒng)
  • 超過(guò)請(qǐng)求閾值的請(qǐng)求直接被拒絕了,而不是丟棄,給用戶的體驗(yàn)不好
  • 限流效果與劃分的小周期的數(shù)量有關(guān),但往往很難選取

Java實(shí)現(xiàn)

public class SlidingWindowRateLimiter {
    private final int windowSize;  // 時(shí)間窗口大小,單位為秒
    private final int requestLimit;  // 在時(shí)間窗口內(nèi)允許的最大請(qǐng)求數(shù)
    private final LinkedList<Long> timestamps;  // 存儲(chǔ)請(qǐng)求的時(shí)間戳

    public SlidingWindowRateLimiter(int windowSize, int requestLimit) {
        this.windowSize = windowSize;
        this.requestLimit = requestLimit;
        this.timestamps = new LinkedList<>();
    }

    public synchronized boolean allowRequest() {
        long currentTime = System.currentTimeMillis() / 1000;  // 獲取當(dāng)前時(shí)間戳(秒)

        // 移除時(shí)間窗口之外的時(shí)間戳
        while (!timestamps.isEmpty() && timestamps.getFirst() <= currentTime - windowSize) {
            timestamps.removeFirst();
        }

        // 檢查當(dāng)前請(qǐng)求數(shù)是否超過(guò)限制
        if (timestamps.size() < requestLimit) {
            timestamps.addLast(currentTime);
            return true;  // 允許請(qǐng)求
        } else {
            return false;  // 請(qǐng)求超過(guò)限制
        }
    }

    public static void main(String[] args) {
        SlidingWindowRateLimiter rateLimiter = new SlidingWindowRateLimiter(10, 2);

        // 模擬請(qǐng)求
        for (int i = 0; i < 10; i++) {
            boolean allowed = rateLimiter.allowRequest();
            System.out.println("請(qǐng)求 " + (i + 1) + ": " + (allowed ? "被允許" : "被拒絕"));
        }
    }
}

結(jié)果:

漏桶限流

漏桶限流算法又算是解決了固定窗口限流和滑動(dòng)窗口限流出現(xiàn)的請(qǐng)求被拒絕的問(wèn)題,其對(duì)于超出請(qǐng)求閾值的請(qǐng)求會(huì)直接丟棄。

水(請(qǐng)求)以任意的速率流入桶內(nèi),以固定的速率從桶底流出(處理請(qǐng)求),如果水的流入速度大于流出速度(系統(tǒng)請(qǐng)求速度大于處理請(qǐng)求的速度),水就會(huì)溢出(即請(qǐng)求直接被丟棄)。

優(yōu)點(diǎn):

  • 解決了請(qǐng)求被直接拒絕的問(wèn)題
  • 一定程度上解決了臨界問(wèn)題(流量突刺)

缺點(diǎn):不能迅速處理一批請(qǐng)求(并發(fā)處理請(qǐng)求),因?yàn)樗牧鞒鏊俾适枪潭ǖ模ㄕ?qǐng)求的處理速率是固定的,只能處理一個(gè)請(qǐng)求后再去處理下一個(gè))

Java實(shí)現(xiàn):

package DataStructure.RateLimtAlgorithm;

import java.time.Instant;
import java.util.concurrent.atomic.AtomicLong;

public class LeakyBucketRateLimiter {
    private final int capacity; // 桶的容量
    private final int rate; // 漏水速率,單位:請(qǐng)求/秒
    private AtomicLong water; // 當(dāng)前桶中的水量
    private Instant lastLeakTime; // 上一次漏水的時(shí)間點(diǎn)

    public LeakyBucketRateLimiter(int capacity, int rate) {
        this.capacity = capacity;
        this.rate = rate;
        this.water = new AtomicLong(0);
        this.lastLeakTime = Instant.now();
    }

    public synchronized boolean allowed() {
        leak(); // 漏水
        if (water.get() < capacity) {
            water.incrementAndGet();
            return true; // 桶未滿,允許請(qǐng)求通過(guò)
        } else {
            return false; // 桶已滿,拒絕請(qǐng)求
        }
    }

    private synchronized void leak() {
        Instant now = Instant.now();
        long interval = now.getEpochSecond() - lastLeakTime.getEpochSecond();
        long leakedWater = interval * rate; // 計(jì)算漏水的數(shù)量
        if (leakedWater > 0) {
            water.set(Math.max(0, water.get() - leakedWater)); // 更新桶中的水量
            lastLeakTime = now; // 更新上一次漏水的時(shí)間點(diǎn)
        }
    }

    public static void main(String[] args) {
        LeakyBucketRateLimiter leakyBucketRateLimiter = new LeakyBucketRateLimiter(5, 2); // 創(chuàng)建一個(gè)容量為10,速率為1個(gè)請(qǐng)求/秒的漏桶
        for (int i = 0; i < 10; i++) {
            boolean allowed = leakyBucketRateLimiter.allowed();
            System.out.println("請(qǐng)求 " + (i + 1) + ": " + (allowed ? "被允許" : "被拒絕"));
            try {
                Thread.sleep(100); // 模擬請(qǐng)求間隔
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

結(jié)果:

令牌桶限流

根據(jù)請(qǐng)求的數(shù)量多少,管理員勻速生成一批令牌。只有擁有令牌的請(qǐng)求才會(huì)被允許,否則就會(huì)被丟棄,不予通過(guò)。比如管理員生成了5個(gè)令牌,此時(shí)來(lái)了10個(gè)請(qǐng)求,那么拿到令牌的5個(gè)請(qǐng)求會(huì)被同時(shí)處理,另外5個(gè)請(qǐng)求就會(huì)被丟棄。

一般令牌桶限流可以通過(guò)Redisson限流器實(shí)現(xiàn),因此其適用于分布式系統(tǒng),當(dāng)然前面三種限流算也可用于分布式系統(tǒng)但是需要手動(dòng)實(shí)現(xiàn)。既然令牌桶限流算法這么好,Redisson還幫我們實(shí)現(xiàn)了,所以一般我們都會(huì)選擇它,因?yàn)閷?zhuān)業(yè) [doge]。

優(yōu)點(diǎn):以上三種算法的所有優(yōu)點(diǎn),并且支持并發(fā)處理請(qǐng)求。

缺點(diǎn):

  • 不適合長(zhǎng)時(shí)間限流
  • 對(duì)資源消耗高,依賴Redis實(shí)現(xiàn)
  • 不適用于突發(fā)性任務(wù)

Java實(shí)現(xiàn):

@Service
public class RedisLimiterManager {

    @Resource
    private RedissonClient redissonClient;

    /**
     * 限流操作
     *
     * @param key 區(qū)分不同的限流器,比如不同的用戶 id 應(yīng)該分別統(tǒng)計(jì)
     */
    public void doRateLimit(String key) {
        // 創(chuàng)建一個(gè)名稱為user_limiter的限流器,每秒最多訪問(wèn)2次
        RRateLimiter rateLimiter = redissonClient.getRateLimiter(key);
        rateLimiter.trySetRate(RateType.OVERALL, 2, 1, RateIntervalUnit.SECONDS); // OVERALL類(lèi)型:不管有多少臺(tái)服務(wù)器都是放在一起統(tǒng)計(jì)的,請(qǐng)求閾值,生成令牌的頻率
        // 每當(dāng)一個(gè)操作來(lái)了后,請(qǐng)求一個(gè)令牌
        boolean canOp = rateLimiter.tryAcquire(1);// 令牌請(qǐng)求數(shù),處理請(qǐng)求需消耗的令牌
        if (!canOp) {
            throw new BusinessException(ErrorCode.TOO_MANY_REQUEST);
        }
    }
}

@SpringBootTest
class RedisLimiterManagerTest {
    @Resource
    private RedisLimiterManager redisLimiterManager;

    @Test
    void doRateLimit() throws InterruptedException {
        String userId = "1";
        for (int i=0;i<10;i++) {
            redisLimiterManager.doRateLimit(userId);
            System.out.println("success");
        }
    }
}

結(jié)果:

參考來(lái)源:面試必備:4種經(jīng)典限流算法講解 - 掘金

到此這篇關(guān)于Java代碼實(shí)現(xiàn)四種限流算法詳細(xì)介紹的文章就介紹到這了,更多相關(guān)Java 限流算法內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 詳解SpringMVC中的@RequestMapping注解

    詳解SpringMVC中的@RequestMapping注解

    這篇文章主要介紹了SpringMVC中@RequestMapping注解,@RequestMapping注解是一個(gè)用來(lái)處理請(qǐng)求地址映射的注解,可用于映射一個(gè)請(qǐng)求或一個(gè)方法,可以用在類(lèi)或方法上,需要的朋友可以參考下
    2023-07-07
  • 詳解在SpringBoot中使用MongoDb做單元測(cè)試的代碼

    詳解在SpringBoot中使用MongoDb做單元測(cè)試的代碼

    這篇文章主要介紹了詳解在SpringBoot中使用MongoDb做單元測(cè)試的代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-11-11
  • 詳解Java數(shù)組的排序算法與二分查找法

    詳解Java數(shù)組的排序算法與二分查找法

    這篇文章詳細(xì)給大家介紹了Java數(shù)組的排序算法與二分查找法,文中有詳細(xì)的代碼示例,對(duì)大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下
    2023-05-05
  • 詳解Spring Cloud 跨服務(wù)數(shù)據(jù)聚合框架

    詳解Spring Cloud 跨服務(wù)數(shù)據(jù)聚合框架

    這篇文章主要介紹了詳解Spring Cloud 跨服務(wù)數(shù)據(jù)聚合框架,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-03-03
  • 淺談Java double 相乘的結(jié)果偏差小問(wèn)題

    淺談Java double 相乘的結(jié)果偏差小問(wèn)題

    下面小編就為大家?guī)?lái)一篇淺談Java double 相乘的結(jié)果偏差小問(wèn)題。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-01-01
  • springboot整合solr的方法詳解

    springboot整合solr的方法詳解

    這篇文章主要介紹了springboot整合solr的方法詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-08-08
  • 詳解Java中異步轉(zhuǎn)同步的六種方法

    詳解Java中異步轉(zhuǎn)同步的六種方法

    針對(duì)應(yīng)用中異步調(diào)用,能不能像同步調(diào)用一樣立刻獲取到命令的執(zhí)行結(jié)果,如何實(shí)現(xiàn)異步轉(zhuǎn)同步?不要擔(dān)心,本文就來(lái)為大家詳細(xì)講講Java中異步轉(zhuǎn)同步的六種方法,感興趣的可以了解一下
    2022-06-06
  • 關(guān)于SpringBoot啟動(dòng)速度慢的原因總結(jié)

    關(guān)于SpringBoot啟動(dòng)速度慢的原因總結(jié)

    這篇文章主要介紹了關(guān)于SpringBoot啟動(dòng)速度慢的原因總結(jié),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-05-05
  • 解決IDEA中maven導(dǎo)入jar包一直報(bào)錯(cuò)問(wèn)題

    解決IDEA中maven導(dǎo)入jar包一直報(bào)錯(cuò)問(wèn)題

    這篇文章主要介紹了解決IDEA中maven導(dǎo)入jar包一直報(bào)錯(cuò)問(wèn)題,本文通過(guò)實(shí)例圖文的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-04-04
  • Java結(jié)構(gòu)型設(shè)計(jì)模式之享元模式示例詳解

    Java結(jié)構(gòu)型設(shè)計(jì)模式之享元模式示例詳解

    享元模式(FlyWeight?Pattern),也叫蠅量模式,運(yùn)用共享技術(shù),有效的支持大量細(xì)粒度的對(duì)象,享元模式就是池技術(shù)的重要實(shí)現(xiàn)方式。本文將通過(guò)示例詳細(xì)講解享元模式,感興趣的可以了解一下
    2022-09-09

最新評(píng)論