限流的介紹以及限流的代碼實現

限流的介紹以及限流的實現

什麼是限流

限流就是對某一段時間窗口內的請求數進行控制,保持系統的可用性和穩定性。
防止因爲流量的暴增而導致系統奔潰。

很多平臺都有限流,例如我們調用第三方的api的時候,對方都會限制每天最多隻能調用幾次之類的。

常用的限流方式

1)信號量
2)令牌桶

信號量

Semaphore是一個計數信號量。常用於限制獲取某資源的線程數量,
可用java的Semaphore 實現。

原理就是先定義總共有多少個信號量,然後每個線程進來拿到幾個信號量纔可以成功訪問,這樣就允許了同時有多少個線程訪問。

例如,定義了6個信號量,每個線程進來拿2個,那麼同時允許3個線程成功訪問。

通過acquire()方法獲取許可,該方法會阻塞,直到獲取許可爲止。
通過release()方法釋放許可。

代碼實現如下

@RestController
@RequestMapping("/semaphore")
public class SemaphoreController {
    //初始化信號量=5,true-公平信號量FIFO。
    private Semaphore semaphore = new Semaphore(5, true);

	//該方法是阻塞的,直到獲取許可爲止
    @RequestMapping("/acquire/{par1}")
    public String acquire(@PathVariable String par1){
        try {
            semaphore.acquire(1);//獲取一個許可
            //
            return par1+"獲取一個許可";
        }catch (InterruptedException e){
            e.printStackTrace();
            return "獲取許可失敗";
        }catch (Exception e1){
            return "獲取許可失敗";
        }finally {
            semaphore.release(1);//處理完後,釋放一個許可
        }
    }
}

令牌桶

首先看下來自網絡的圖片

在這裏插入圖片描述
在這裏插入圖片描述
令牌桶法的過程大概如下:
1)程序按照一定的速度生成令牌,並且放到令牌桶中;

2)令牌桶容量是一定的,如果桶滿了,就丟掉多餘的令牌;

3)如果桶沒滿,就繼續放令牌

4)請求過來的時候,拿到令牌就請求接口成功,拿不到令牌就拒絕訪問。

令牌桶的實現谷歌的guava已經幫我們實現了,就是谷歌的 RateLimiter
接下來,我們看下代碼實現:

	//每秒發放2個令牌
	private static final RateLimiter rateLimiter = RateLimiter.create(2);

	//以下是測試限流的接口
	//一般是採用阻塞和非阻塞的方式來獲取令牌,建議採用非阻塞。


	/**
	 * 非阻塞方式
	 * 嘗試獲取令牌,默認超時時間是0,拿不到就立即返回false
	 */
	public String rateLimiterNonBlock1(){
		if (rateLimiter.tryAcquire()) {//tryAcquire嘗試獲取令牌,默認超時時間是0,意思是拿不到就立即返回false。而且tryAcquire()只拿一個令牌

			try {
				//模擬操作。
				Thread.sleep(500);
			}catch (Exception e){
				e.printStackTrace();
			}


		}else{//拿不到令牌
			return "拿不到令牌,暫時無法訪問。";
		}

		return "正常拿到了令牌,成功訪問";
	}

	/**
	 * 非阻塞方式
	 * 嘗試獲取令牌,超時時間是1秒,如果在1000ms內無法獲取令牌,則直接返回false,無需等待
	 */
	public String rateLimiterNonBlock2() {
		//如果令牌桶內有令牌,則直接發放;判斷如果能在1000ms內能夠獲取一個令牌,則等待到時後發放;如果在1000ms內無法獲取令牌,則直接返回false,無需等待。
		if (rateLimiter.tryAcquire(1000, TimeUnit.MILLISECONDS)) {
			//do sth
			return "獲取令牌成功";
		} else {
			// do other
			return "獲取令牌失敗";
		}
	}

	/**
	 * 阻塞方式
	 * acquire拿不到就等待,直到一次拿到5個令牌爲止
	 */
	public String reateLimiterBlock(){
		rateLimiter.acquire(5); //  一次拿5個
		return "";
	}

注意:!!!!!
tryAcquire 並不是真正等待timeout時間,而是計算判斷在timeout時間內能否拿到令牌,如果不能就返回false。這是一個挺複雜的數學問題,每一個請求都會被計算未來可能獲取令牌的概率。還好谷歌幫我們實現了這個判斷。

令牌桶也有大神封裝好的可以直接用的,而且還封裝有redis實現的分佈式的限流方式。見下面傳送門。
apiBoot項目傳送門
apiBoot文檔傳送門

如果有幫助到你,就關注或者點贊吧。感謝~~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章