Java 限流策略

概要

在大數據量高併發訪問時,常常會出現服務或接口面對暴漲的請求而不可用的狀況,甚至引起連鎖反映致使整個系統崩潰。此時你須要使用的技術手段之一就是限流,當請求達到必定的併發數或速率,就進行等待、排隊、降級、拒絕服務等。在限流時,常見的兩種算法是漏桶和令牌桶算法。算法

限流算法

令牌桶(Token Bucket)、漏桶(leaky bucket)和計數器算法是最經常使用的三種限流的算法。數據庫

1. 令牌桶算法

令牌桶算法的原理是系統會以一個恆定的速度往桶裏放入令牌,而若是請求須要被處理,則須要先從桶裏獲取一個令牌,當桶裏沒有令牌可取時,則拒絕服務。 當桶滿時,新添加的令牌被丟棄或拒絕。緩存

令牌桶算法示例

public class RateLimiterDemo {
    private static RateLimiter limiter = RateLimiter.create(5);

    public static void exec() {
        limiter.acquire(1);
        try {
            // 處理核心邏輯
            TimeUnit.SECONDS.sleep(1);
            System.out.println("--" + System.currentTimeMillis() / 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Guava RateLimiter 提供了令牌桶算法可用於平滑突發限流策略。
該示例爲每秒中產生5個令牌,每200毫秒會產生一個令牌。
limiter.acquire() 表示消費一個令牌。當桶中有足夠的令牌時,則直接返回0,不然阻塞,直到有可用的令牌數才返回,返回的值爲阻塞的時間。服務器

2. 漏桶算法

它的主要目的是控制數據注入到網絡的速率,平滑網絡上的突發流量,數據能夠以任意速度流入到漏桶中。漏桶算法提供了一種機制,經過它,突發流量能夠被整形以便爲網絡提供一個穩定的流量。 漏桶能夠看做是一個帶有常量服務時間的單服務器隊列,若是漏桶爲空,則不須要流出水滴,若是漏桶(包緩存)溢出,那麼水滴會被溢出丟棄。網絡

3. 計數器限流算法

計數器限流算法也是比較經常使用的,主要用來限制總併發數,好比數據庫鏈接池大小、線程池大小、程序訪問併發數等都是使用計數器算法。併發

使用計數器限流示例1

public class CountRateLimiterDemo1 {

    private static AtomicInteger count = new AtomicInteger(0);

    public static void exec() {
        if (count.get() >= 5) {
            System.out.println("請求用戶過多,請稍後在試!"+System.currentTimeMillis()/1000);
        } else {
            count.incrementAndGet();
            try {
                //處理核心邏輯
                TimeUnit.SECONDS.sleep(1);
                System.out.println("--"+System.currentTimeMillis()/1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                count.decrementAndGet();
            }
        }
    }
}

使用AomicInteger來進行統計當前正在併發執行的次數,若是超過域值就簡單粗暴的直接響應給用戶,說明系統繁忙,請稍後再試或其它跟業務相關的信息。高併發

弊端:使用 AomicInteger 簡單粗暴超過域值就拒絕請求,可能只是瞬時的請求量高,也會拒絕請求。大數據

使用計數器限流示例2

public class CountRateLimiterDemo2 {

    private static Semaphore semphore = new Semaphore(5);

    public static void exec() {
        if(semphore.getQueueLength()>100){
            System.out.println("當前等待排隊的任務數大於100,請稍候再試...");
        }
        try {
            semphore.acquire();
            // 處理核心邏輯
            TimeUnit.SECONDS.sleep(1);
            System.out.println("--" + System.currentTimeMillis() / 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            semphore.release();
        }
    }
}

使用Semaphore信號量來控制併發執行的次數,若是超過域值信號量,則進入阻塞隊列中排隊等待獲取信號量進行執行。若是阻塞隊列中排隊的請求過多超出系統處理能力,則能夠在拒絕請求。ui

相對Atomic優勢:若是是瞬時的高併發,能夠使請求在阻塞隊列中排隊,而不是立刻拒絕請求,從而達到一個流量削峯的目的。spa

相關文章
相關標籤/搜索