最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java中常見的4種限流算法詳解

 更新時(shí)間:2023年12月21日 09:00:55   作者:Colins~  
這篇文章主要介紹了Java中常見的4種限流算法詳解,FixedWindowRateLimiter 類表示一個(gè)固定窗口限流器,使用 limit 和 interval 參數(shù)分別表示限制請(qǐng)求數(shù)量和時(shí)間間隔,缺點(diǎn)是短時(shí)間內(nèi)可能會(huì)流量翻倍,需要的朋友可以參考下

固定窗口

FixedWindowRateLimiter 類表示一個(gè)固定窗口限流器,使用 limit 和 interval 參數(shù)分別表示限制請(qǐng)求數(shù)量和時(shí)間間隔(毫秒)。在 allowRequest() 方法中,通過(guò)比較當(dāng)前時(shí)間與上一次請(qǐng)求時(shí)間來(lái)判斷是否需要重置請(qǐng)求數(shù)和上一次請(qǐng)求時(shí)間。如果請(qǐng)求數(shù)還沒(méi)有達(dá)到限制數(shù)量,允許請(qǐng)求并增加請(qǐng)求數(shù),否則拒絕請(qǐng)求。 缺點(diǎn):短時(shí)間內(nèi)可能會(huì)流量翻倍

public class FixedWindowRateLimiter {
    private final int limit; // 限制請(qǐng)求數(shù)量
    private final AtomicInteger count; // 當(dāng)前請(qǐng)求數(shù)
    private final long interval; // 時(shí)間間隔(毫秒)
    private long lastRequestTime; // 上一次請(qǐng)求時(shí)間
    public FixedWindowRateLimiter(int limit, long interval) {
        this.limit = limit;
        this.interval = interval;
        this.count = new AtomicInteger(0);
        this.lastRequestTime = System.currentTimeMillis();
    }
    public synchronized boolean allowRequest() {
        long currentTime = System.currentTimeMillis();
        if (currentTime - lastRequestTime > interval) {
            // 如果距離上一次請(qǐng)求時(shí)間已經(jīng)超過(guò)了時(shí)間間隔,重置請(qǐng)求數(shù)和上一次請(qǐng)求時(shí)間
            count.set(0);
            lastRequestTime = currentTime;
        }
        // 如果請(qǐng)求數(shù)還沒(méi)有達(dá)到限制數(shù)量,允許請(qǐng)求并增加請(qǐng)求數(shù)
        if (count.get() < limit) {
            count.incrementAndGet();
            return true;
        }
        return false; // 否則拒絕請(qǐng)求
    }
}
// 使用示例
FixedWindowRateLimiter limiter = new FixedWindowRateLimiter(10, 1000); // 每秒最多處理10個(gè)請(qǐng)求
for (int i = 0; i < 20; i++) { // 嘗試發(fā)起20個(gè)請(qǐng)求
    if (limiter.allowRequest()) {
        System.out.println("Allow request " + i);
    } else {
        System.out.println("Reject request " + i);
    }
    Thread.sleep(200); // 每次請(qǐng)求間隔200毫秒
}

滑動(dòng)窗口

相比于固定窗口,滑動(dòng)窗口有以下幾個(gè)好處:

  1. 平滑限流:滑動(dòng)窗口限流算法會(huì)以時(shí)間為軸將請(qǐng)求限制平均到每個(gè)時(shí)間段內(nèi),從而平滑了請(qǐng)求的涌入。相比于簡(jiǎn)單粗暴的限制請(qǐng)求的數(shù)量或速率等方式,這種平滑的限流方式能夠更好地保證服務(wù)的可用性和穩(wěn)定性。
  2. 精確控制:滑動(dòng)窗口限流算法可以根據(jù)具體的業(yè)務(wù)需要設(shè)置窗口大小和時(shí)間間隔,從而實(shí)現(xiàn)對(duì)請(qǐng)求的精確控制。通過(guò)適當(dāng)調(diào)整窗口大小和時(shí)間間隔,可以達(dá)到更好的限流效果。

關(guān)于這個(gè),我覺(jué)得sentinel中的滑動(dòng)窗口就非常的nice,下面是從sentinel中摘出來(lái)改一下的示例(同時(shí)也運(yùn)用在我本人的中間件內(nèi)),總得來(lái)說(shuō)有三部分:

  • 窗口存放的實(shí)體類(監(jiān)控指標(biāo)) HystrixEntity
  • 窗口的定義 HystrixWindow
  • 滑動(dòng)窗口具體實(shí)現(xiàn) HystrixWindowArray
// 窗口存放的實(shí)體類(監(jiān)控指標(biāo))
public class HystrixEntity {
    // 窗口請(qǐng)求數(shù)
    private AtomicInteger requestCount;
    // 窗口異常數(shù)
    private AtomicInteger errorCount;
    public HystrixEntity(){
        this.requestCount=new AtomicInteger(0);
        this.errorCount=new AtomicInteger(0);
    }
    public int getRequestCountValue() {
        return requestCount.get();
    }
    public int getErrorCountValue() {
        return errorCount.get();
    }
    public void resetValue() {
        this.errorCount.set(0);
        this.requestCount.set(0);
    }
    public void addErrorCount(){
        this.errorCount.addAndGet(1);
    }
    public void addRequestCount(){
        this.requestCount.addAndGet(1);
    }
}
//窗口的定義 HystrixWindow
public class HystrixWindow {
    // 窗口的長(zhǎng)度 單位:ms
    private final int windowLengthInMs;
    // 窗口的開始時(shí)間戳  單位:ms
    private long windowStartInMs;
    // 窗口內(nèi)存放的實(shí)體類
    private HystrixEntity hystrixEntity;
    public HystrixWindow(int windowLengthInMs, long windowStartInMs, HystrixEntity hystrixEntity) {
        this.windowLengthInMs = windowLengthInMs;
        this.windowStartInMs = windowStartInMs;
        this.hystrixEntity = hystrixEntity;
    }
    public int getWindowLengthInMs() {
        return windowLengthInMs;
    }
    public long getWindowStartInMs() {
        return windowStartInMs;
    }
    public HystrixEntity getHystrixEntity() {
        return hystrixEntity;
    }
    public void setHystrixEntity(HystrixEntity hystrixEntity) {
        this.hystrixEntity = hystrixEntity;
    }
    /**
     * @Description 重置窗口
     **/
    public HystrixWindow resetTo(long startTime) {
        this.windowStartInMs = startTime;
        hystrixEntity.resetValue();
        return this;
    }
    /**
     * @Description 判斷時(shí)間是否屬于該窗口
     **/
    public boolean isTimeInWindow(long timeMillis) {
        return windowStartInMs <= timeMillis && timeMillis < windowStartInMs + windowLengthInMs;
    }
}
//滑動(dòng)窗口具體實(shí)現(xiàn)
public class HystrixWindowArray {
    // 單個(gè)窗口的長(zhǎng)度
    private int windowLengthInMs;
    // 窗口數(shù)量
    private int sampleCount;
    // 所有窗口的總長(zhǎng)度
    private int intervalInMs;
    // 窗口數(shù)組
    private final AtomicReferenceArray<HystrixWindow> array;
    /**
     * The conditional (predicate) update lock is used only when current bucket is deprecated.
     */
    private final ReentrantLock updateLock = new ReentrantLock();
    /**
     * @Param [sampleCount, intervalInMs]
     * sampleCount: 窗口數(shù)量  intervalInMs:所有窗口的總長(zhǎng)度
     **/
    public HystrixWindowArray(int sampleCount, int intervalInMs) {
        Assert.isTrue(sampleCount > 0, "bucket count is invalid: " + sampleCount);
        Assert.isTrue(intervalInMs > 0, "total time interval of the sliding window should be positive");
        Assert.isTrue(intervalInMs % sampleCount == 0, "time span needs to be evenly divided");
        this.windowLengthInMs = intervalInMs / sampleCount;
        this.intervalInMs = intervalInMs;
        this.sampleCount = sampleCount;
        this.array = new AtomicReferenceArray<>(sampleCount);
    }
    /**
     * 獲取當(dāng)前時(shí)間所在的窗口下標(biāo)索引
     */
    private int calculateTimeIdx(long timeMillis) {
        long timeId = timeMillis / windowLengthInMs;
        // Calculate current index so we can map the timestamp to the leap array.
        return (int)(timeId % array.length());
    }
    /**
     * 獲取當(dāng)前時(shí)間所在的窗口開始時(shí)間
     */
    private long calculateWindowStart(long timeMillis) {
        return timeMillis - timeMillis % windowLengthInMs;
    }
    private HystrixEntity newEmptyWindowValue(long timeMillis){
        return new HystrixEntity();
    }
    /**
     * 獲取當(dāng)前窗口
     */
    public HystrixWindow currentWindow() {
        return currentWindow(System.currentTimeMillis());
    }
    private HystrixWindow currentWindow(long timeMillis) {
        if (timeMillis < 0) {
            return null;
        }
        int idx = calculateTimeIdx(timeMillis);
        // Calculate current bucket start time.
        long windowStart = calculateWindowStart(timeMillis);
        while (true) {
            HystrixWindow old = array.get(idx);
            if (old == null) {
                // 如果獲取為空,說(shuō)明窗口還沒(méi)創(chuàng)建,所以我們創(chuàng)建一個(gè)新的窗口(CAS保證線程安全)
                HystrixWindow window = new HystrixWindow(windowLengthInMs, windowStart, newEmptyWindowValue(timeMillis));
                if (array.compareAndSet(idx, null, window)) {
                    // 創(chuàng)建成功則返回當(dāng)前窗口
                    return window;
                } else {
                    // 創(chuàng)建失敗說(shuō)明發(fā)生了競(jìng)爭(zhēng),所以暫時(shí)先讓出CPU
                    Thread.yield();
                }
            } else if (windowStart == old.getWindowStartInMs()) {
                // 如果窗口已經(jīng)存在,則對(duì)比窗口的開始時(shí)間是否相同,相同說(shuō)明是用同一個(gè)窗口,直接返回窗口就可以了
                return old;
            } else if (windowStart > old.getWindowStartInMs()) {
                // 如果窗口已經(jīng)存在,而且窗口開始時(shí)間比之前的窗口開始時(shí)間要大
                // 說(shuō)明原來(lái)的窗口已經(jīng)過(guò)時(shí)了,需要替換一個(gè)新的窗口
                // 所以加鎖防止競(jìng)爭(zhēng)
                if (updateLock.tryLock()) {
                    try {
                        // 這里我選擇直接重置之前的窗口()
                        return resetWindowTo(old, windowStart);
                    } finally {
                        updateLock.unlock();
                    }
                } else {
                    // Contention failed, the thread will yield its time slice to wait for bucket available.
                    Thread.yield();
                }
            } else if (windowStart < old.getWindowStartInMs()) {
                // 窗口的開始時(shí)間比之前的窗口開始時(shí)間還會(huì)小,這種屬于異常情況
                // 要是真出現(xiàn)了也只能新建一個(gè)窗口返回了
                return new HystrixWindow(windowLengthInMs, windowStart, newEmptyWindowValue(timeMillis));
            }
        }
    }
    /**
     * 獲取當(dāng)前窗口內(nèi)的值
     */
    public HystrixEntity getWindowValue() {
        return getWindowValue(System.currentTimeMillis());
    }
    public HystrixEntity getWindowValue(long timeMillis) {
        if (timeMillis < 0) {
            return null;
        }
        int idx = calculateTimeIdx(timeMillis);
        HystrixWindow bucket = array.get(idx);
        if (bucket == null || !bucket.isTimeInWindow(timeMillis)) {
            return null;
        }
        return bucket.getHystrixEntity();
    }
    /**
     * 重置一個(gè)窗口
     */
    private HystrixWindow resetWindowTo(HystrixWindow window, long startTime){
        return window.resetTo(startTime);
    }
    public List<HystrixEntity> values() {
        return values(System.currentTimeMillis());
    }
    private List<HystrixEntity> values(long timeMillis) {
        if (timeMillis < 0) {
            return new ArrayList<HystrixEntity>();
        }
        int size = array.length();
        List<HystrixEntity> result = new ArrayList<HystrixEntity>(size);
        for (int i = 0; i < size; i++) {
            HystrixWindow window = array.get(i);
            if (window == null || isWindowDeprecated(timeMillis, window)) {
                continue;
            }
            result.add(window.getHystrixEntity());
        }
        return result;
    }
    /**
     * 判斷窗口是否有效
     */
    public boolean isWindowDeprecated(long time, HystrixWindow window) {
        return time - window.getWindowStartInMs() > intervalInMs;
    }
}

使用示例:

// 初始化 代表監(jiān)控1s內(nèi)的指標(biāo)  窗口數(shù)為2
HystrixWindowArray hystrixWindowArray = new HystrixWindowArray(2, 1000);
// 指標(biāo)監(jiān)控  數(shù)量+1 
windowArray.currentWindow().getHystrixEntity().addErrorCount();
windowArray.currentWindow().getHystrixEntity().addRequestCount();
// 獲取所有窗口的指標(biāo)累計(jì)  判斷是否超標(biāo),也就是1s內(nèi)的總計(jì)
List<HystrixEntity> windowValues = windowArray.values();
Integer errorCount = hystrixEntities.stream().map(HystrixEntity::getErrorCountValue).reduce(Integer::sum).get();
Integer requestCount = hystrixEntities.stream().map(HystrixEntity::getRequestCountValue).reduce(Integer::sum).get();

令牌桶算法

思想: 固定時(shí)間內(nèi)(例如 1 秒)通過(guò)一個(gè)桶來(lái)存儲(chǔ)令牌,每當(dāng)接收到一個(gè)請(qǐng)求時(shí)就會(huì)消耗一個(gè)令牌。如果請(qǐng)求過(guò)來(lái)時(shí)沒(méi)有令牌,則無(wú)法繼續(xù)處理該請(qǐng)求。在sentinel中被稱為冷啟動(dòng)

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

相比于漏桶算法,令牌桶算法具有更好的適應(yīng)性,可以應(yīng)對(duì)短時(shí)間內(nèi)的流量波動(dòng)。(漏桶算法只能處理恒定速率的流量)

代碼如下:

public class TokenBucket {
    private long lastTime;  // 上次請(qǐng)求時(shí)間
    private double rate;    // 令牌放入速率
    private long capacity;  // 令牌桶容量
    private long tokens;    // 當(dāng)前令牌數(shù)量
    public TokenBucket(double rate, long capacity) {
        this.lastTime = System.currentTimeMillis();
        this.rate = rate;
        this.capacity = capacity;
        this.tokens = capacity;
    }
    public synchronized boolean getToken() {
        long now = System.currentTimeMillis();
        long timeElapsed = now - lastTime;
        tokens += timeElapsed * rate;
        if (tokens > capacity) {
            tokens = capacity;
        }
        lastTime = now;
        if (tokens >= 1) {
            tokens--;
            return true;
        } else {
            return false;
        }
    }
}

漏斗桶算法

漏桶算法(Leaky Bucket)是網(wǎng)絡(luò)世界中流量整形(Traffic Shaping)或速率限制(Rate Limiting)時(shí)經(jīng)常使用的一種算法,它的主要目的是控制數(shù)據(jù)注入到網(wǎng)絡(luò)的速率,平滑網(wǎng)絡(luò)上的突發(fā)流量。漏桶算法提供了一種機(jī)制,通過(guò)它,突發(fā)流量可以被整形以便為網(wǎng)絡(luò)提供一個(gè)穩(wěn)定的流量。在sentinel中被稱為勻速器

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

  1. 控制速率:漏斗桶算法可以限制數(shù)據(jù)流量的傳輸速率,確保各個(gè)環(huán)節(jié)的數(shù)據(jù)處理能力都得到了滿足,從而避免了系統(tǒng)因?yàn)閿?shù)據(jù)過(guò)多而導(dǎo)致的崩潰和癱瘓。
  2. 平滑輸出:漏斗桶算法通過(guò)將數(shù)據(jù)分散到不同的時(shí)間段內(nèi)進(jìn)行處理,使得數(shù)據(jù)傳輸?shù)妮敵龈悠椒€(wěn),不會(huì)出現(xiàn)明顯的波動(dòng),提高了網(wǎng)絡(luò)的穩(wěn)定性。

代碼如下:

public class LeakyBucket {
    private int capacity; //漏桶容量
    private int rate; //漏水速率
    private int water; //當(dāng)前水量
    private Instant timestamp; //上次漏水時(shí)間
    public LeakyBucket(int capacity, int rate) {
        this.capacity = capacity;
        this.rate = rate;
        this.water = 0;
        this.timestamp = Instant.now();
    }
    public synchronized boolean allow() { //判斷是否允許通過(guò)
        Instant now = Instant.now();
        long duration = now.toEpochMilli() - timestamp.toEpochMilli(); //計(jì)算距上次漏水過(guò)去了多久
        int outflow = (int) (duration * rate / 1000); //計(jì)算過(guò)去的時(shí)間內(nèi)漏出的水量
        water = Math.max(0, water - outflow); //更新當(dāng)前水量,不能小于0
        if (water < capacity) { //如果漏桶還沒(méi)滿,放行
            water++;
            timestamp = now;
            return true;
        }
        return false; //否則拒絕通過(guò)
    }
}

到此這篇關(guān)于Java中常見的4種限流算法詳解的文章就介紹到這了,更多相關(guān)Java限流算法內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java 判斷線程池所有任務(wù)是否執(zhí)行完畢的操作

    Java 判斷線程池所有任務(wù)是否執(zhí)行完畢的操作

    這篇文章主要介紹了Java 判斷線程池所有任務(wù)是否執(zhí)行完畢的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-08-08
  • Java數(shù)組的特性_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    Java數(shù)組的特性_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    數(shù)組是基本上所有語(yǔ)言都會(huì)有的一種數(shù)據(jù)類型,它表示一組相同類型的數(shù)據(jù)的集合,具有固定的長(zhǎng)度,并且在內(nèi)存中占據(jù)連續(xù)的空間。在C,C++等語(yǔ)言中,數(shù)組的定義簡(jiǎn)潔清晰,而在Java中確有一些會(huì)讓人迷惑的特性。本文就嘗試分析這些特性
    2017-04-04
  • Java集合之Disruptor操作示例

    Java集合之Disruptor操作示例

    這篇文章主要為大家介紹了Java集合之Disruptor操作示例介紹,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-08-08
  • java調(diào)用未知類的指定方法簡(jiǎn)單實(shí)例

    java調(diào)用未知類的指定方法簡(jiǎn)單實(shí)例

    這篇文章介紹了java調(diào)用未知類的指定方法簡(jiǎn)單實(shí)例,有需要的朋友可以參考一下
    2013-09-09
  • 使用maven的profile構(gòu)建不同環(huán)境配置的方法

    使用maven的profile構(gòu)建不同環(huán)境配置的方法

    這篇文章主要介紹了使用maven的profile構(gòu)建不同環(huán)境配置的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-01-01
  • maven項(xiàng)目在實(shí)踐中的構(gòu)建管理之路的方法

    maven項(xiàng)目在實(shí)踐中的構(gòu)建管理之路的方法

    這篇文章主要介紹了maven項(xiàng)目在實(shí)踐中的構(gòu)建管理之路的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2019-05-05
  • Java的SPI機(jī)制實(shí)例詳解

    Java的SPI機(jī)制實(shí)例詳解

    這篇文章主要介紹了Java的SPI機(jī)制實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下
    2017-06-06
  • 詳解怎么用Java的super關(guān)鍵字

    詳解怎么用Java的super關(guān)鍵字

    今天帶大家學(xué)習(xí)Java中super關(guān)鍵字是怎么用的,文中有非常詳細(xì)的介紹,對(duì)正在學(xué)習(xí)的小伙伴們很有幫助,需要的朋友可以參考下
    2021-06-06
  • Spring使用@Autowired注解實(shí)現(xiàn)自動(dòng)裝配方式

    Spring使用@Autowired注解實(shí)現(xiàn)自動(dòng)裝配方式

    這篇文章主要介紹了Spring使用@Autowired注解實(shí)現(xiàn)自動(dòng)裝配方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • MyBatis中基于別名typeAliases的設(shè)置

    MyBatis中基于別名typeAliases的設(shè)置

    這篇文章主要介紹了MyBatis中基于別名typeAliases的設(shè)置,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-07-07

最新評(píng)論

遂溪县| 平果县| 丰都县| 元谋县| 东乡族自治县| 东光县| 贵溪市| 遂平县| 吴江市| 通辽市| 离岛区| 渭源县| 乃东县| 吴川市| 盖州市| 时尚| 靖边县| 通海县| 来宾市| 法库县| 略阳县| 渑池县| 云林县| 永德县| 金乡县| 阳城县| 屯昌县| 探索| 虹口区| 安新县| 仙居县| 革吉县| 南平市| 太白县| 黔东| 卢氏县| 永寿县| 石柱| 绩溪县| 得荣县| 拉孜县|