Java限流詳解

一、限流概述

隨著互聯網化程度的不斷提高,大量用戶同時使用某些功能,可能會導致某些功能被耗盡,從而導致系統癱瘓。為了應對這種情況,限流成為了解決問題的重要手段。限流是指根據給定的規則和策略來控制、限制應用程序流量,以達到保護後端應用的目的。Java作為一門流行的編程語言,提供了多種限流機制,可以解決大流量並發任務下伺服器資源被耗光的問題。

二、限流分類

限流根據限流目的和實現方式的不同,可以分為五種類型:

1. 固定窗口演算法

固定窗口演算法限制時間窗口內的請求數量,同一時間窗口內的請求量不能超過設定的閾值。

public class FixedWindowRateLimiter implements RateLimiter {
    private AtomicInteger counter = new AtomicInteger(0);
    private final int limit;
    private final int intervalMs;
    private final ThreadLocal last = new ThreadLocal() {
        @Override
        protected Long initialValue() {
            return System.currentTimeMillis();
        }
    };
    public FixedWindowRateLimiter(int limit, int intervalMs) {
        this.limit = limit;
        this.intervalMs = intervalMs;
    }
    @Override
    public boolean tryAcquire() {
        long now = System.currentTimeMillis();
        long lastTime = last.get();
        if (now - lastTime > intervalMs) {
            counter.set(0);
            last.set(now);
        }
        return counter.incrementAndGet() <= limit;
    }
}

2. 滑動窗口演算法

滑動窗口演算法仍然限制時間窗口內的請求數量,不同的是時間窗口可以滑動,即滑動窗口演算法最近的一段時間窗口都被重複使用。

public class SlidingWindowRateLimiter implements RateLimiter {
    private AtomicInteger counter = new AtomicInteger(0);
    private final int limit;
    private final int intervalMs;
    private final int bucketSize;
    private final ConcurrentLinkedDeque windows = new ConcurrentLinkedDeque();
    public SlidingWindowRateLimiter(int limit, int intervalMs, int bucketSize) {
        this.limit = limit;
        this.intervalMs = intervalMs;
        this.bucketSize = bucketSize;
    }
    @Override
    public boolean tryAcquire() {
        long now = System.currentTimeMillis();
        if (!windows.isEmpty() && now - windows.peek() > intervalMs) {
            int size = windows.size();
            for (int i = 0; i < size && i < bucketSize; i++) {
                windows.poll();
            }
            counter.set(windows.size());
        }
        if (counter.incrementAndGet() <= limit) {
            windows.offer(System.currentTimeMillis());
            return true;
        }
        counter.decrementAndGet();
        return false;
    }
}

3. Leaky Bucket演算法

Leaky Bucket演算法是一種限制請求流量的演算法,它通過給定最大速率控制進入網路的數據數量,有效地防止了請求峰的出現,從而保護了系統。

public class LeakyBucketRateLimiter implements RateLimiter {
    private AtomicInteger counter = new AtomicInteger(0);
    private final int limit;
    private final int intervalMs;
    private final float leakRate;
    private AtomicLong leftWater = new AtomicLong(0);
    private AtomicLong lastUpdateTime = new AtomicLong(System.nanoTime());
    public LeakyBucketRateLimiter(int limit, int intervalMs, float leakRate) {
        this.limit = limit;
        this.intervalMs = intervalMs;
        this.leakRate = leakRate;
    }
    @Override
    public boolean tryAcquire() {
        long now = System.nanoTime();
        long timeDelta = now - lastUpdateTime.get();
        long left = leftWater.addAndGet((long) (-timeDelta * leakRate));
        if (left < 0) {
            left = 0;
            leftWater.set(left);
        }
        if (left < limit) {
            if (counter.addAndGet(1) <= limit) {
                return true;
            } else {
                counter.decrementAndGet();
            }
        }
        lastUpdateTime.set(now);
        return false;
    }
}

4. Token Bucket演算法

Token Bucket演算法是一種計量允許突發請求流量的演算法,它通過對請求進行統計並抑制突發請求流量,保護系統正常運行。

public class TokenBucketRateLimiter implements RateLimiter {
    private AtomicInteger tokens = new AtomicInteger(0);
    private final int limit;
    private final int intervalMs;
    private final int refillTimeMs;
    private final int refillCount;
    private final ThreadLocal last = new ThreadLocal() {
        @Override
        protected Long initialValue() {
            return System.currentTimeMillis();
        }
    };
    public TokenBucketRateLimiter(int limit, int intervalMs, int refillTimeSec, int refillCount) {
        this.limit = limit;
        this.intervalMs = intervalMs;
        this.refillTimeMs = refillTimeSec * 1000;
        this.refillCount = refillCount;
    }
    @Override
    public boolean tryAcquire() {
        long now = System.currentTimeMillis();
        int currentCount = tokens.get();
        if (now - last.get() > refillTimeMs) {
            int newTokens = (int) ((now - last.get()) / refillTimeMs * refillCount);
            if (newTokens > 0) {
                tokens.set(Math.min(currentCount + newTokens, limit));
                last.set(now);
            }
        }
        return tokens.compareAndSet(currentCount, currentCount - 1);
    }
}

5. 基於Google Guava的流量控制

Google Guava提供了一種基於令牌桶演算法的流控方式,只需定製化配置令牌生成速率和令牌桶大小,即可輕鬆實現流控。

private static final RateLimiter RATE_LIMITER = RateLimiter.create(1.0);
public void rateLimiter() {
    if (RATE_LIMITER.tryAcquire()) {
        // Allow access
    } else {
        // Reject request
    }
}

三、應用場景

限流機制可以用於很多場景,例如:高並發請求、海量爬蟲請求、大量資料庫查詢操作等。以下是限流機制的一些具體應用場景:

1. 秒殺系統搶購場景

秒殺場景下用戶同時發起大量請求,伺服器負擔過重,為此可以通過限流措施對在一定時間內訪問的請求進行限制,比如高並發時採用Leaky Bucket演算法進行限流,控制請求流量。

2. 海量爬蟲請求場景

爬蟲請求注意要保證請求合理、搶佔時間均勻,限流是抵抗爬蟲攻擊、保護伺服器資源的重要手段,可以採用固定窗口、滑動窗口等演算法進行限流。

3. 流媒體播放區分不同視頻質量場景

流媒體播放場景下用戶可以通過質量切換功能,切換不同的碼率、解析度、FPS等參數,可以通過Token Bucket演算法或基於Guava的令牌桶演算法實現限流。

四、總結

本文從限流基本概念入手,詳細介紹了Java下的五種常見限流演算法及其應用場景。針對不同的應用場景,可以選擇不同的限流演算法實現或者結合使用多個限流演算法進行流量控制,以達到保護系統的目的。

原創文章,作者:PDYJO,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/325064.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
PDYJO的頭像PDYJO
上一篇 2025-01-13 13:23
下一篇 2025-01-13 13:23

相關推薦

  • java client.getacsresponse 編譯報錯解決方法

    java client.getacsresponse 編譯報錯是Java編程過程中常見的錯誤,常見的原因是代碼的語法錯誤、類庫依賴問題和編譯環境的配置問題。下面將從多個方面進行分析…

    編程 2025-04-29
  • Java JsonPath 效率優化指南

    本篇文章將深入探討Java JsonPath的效率問題,並提供一些優化方案。 一、JsonPath 簡介 JsonPath是一個可用於從JSON數據中獲取信息的庫。它提供了一種DS…

    編程 2025-04-29
  • Java騰訊雲音視頻對接

    本文旨在從多個方面詳細闡述Java騰訊雲音視頻對接,提供完整的代碼示例。 一、騰訊雲音視頻介紹 騰訊雲音視頻服務(Cloud Tencent Real-Time Communica…

    編程 2025-04-29
  • Java Bean載入過程

    Java Bean載入過程涉及到類載入器、反射機制和Java虛擬機的執行過程。在本文中,將從這三個方面詳細闡述Java Bean載入的過程。 一、類載入器 類載入器是Java虛擬機…

    編程 2025-04-29
  • Java Milvus SearchParam withoutFields用法介紹

    本文將詳細介紹Java Milvus SearchParam withoutFields的相關知識和用法。 一、什麼是Java Milvus SearchParam without…

    編程 2025-04-29
  • Java 8中某一周的周一

    Java 8是Java語言中的一個版本,於2014年3月18日發布。本文將從多個方面對Java 8中某一周的周一進行詳細的闡述。 一、數組處理 Java 8新特性之一是Stream…

    編程 2025-04-29
  • Java判斷字元串是否存在多個

    本文將從以下幾個方面詳細闡述如何使用Java判斷一個字元串中是否存在多個指定字元: 一、字元串遍歷 字元串是Java編程中非常重要的一種數據類型。要判斷字元串中是否存在多個指定字元…

    編程 2025-04-29
  • VSCode為什麼無法運行Java

    解答:VSCode無法運行Java是因為默認情況下,VSCode並沒有集成Java運行環境,需要手動添加Java運行環境或安裝相關插件才能實現Java代碼的編寫、調試和運行。 一、…

    編程 2025-04-29
  • Java任務下發回滾系統的設計與實現

    本文將介紹一個Java任務下發回滾系統的設計與實現。該系統可以用於執行複雜的任務,包括可回滾的任務,及時恢復任務失敗前的狀態。系統使用Java語言進行開發,可以支持多種類型的任務。…

    編程 2025-04-29
  • Java 8 Group By 會影響排序嗎?

    是的,Java 8中的Group By會對排序產生影響。本文將從多個方面探討Group By對排序的影響。 一、Group By的概述 Group By是SQL中的一種常見操作,它…

    編程 2025-04-29

發表回復

登錄後才能評論