1. 程式人生 > 資訊 >位元組跳動抖音 App“長輩模式”通過工信部首批適老化及無障礙水平評測

位元組跳動抖音 App“長輩模式”通過工信部首批適老化及無障礙水平評測

限流是對系統的一種保護措施。即限制流量請求的頻率(每秒處理多少個請求)。一般來說,當請求流量超過系統的瓶頸,則丟棄掉多餘的請求流量,保證系統的可用性。即要麼不放進來,放進來的就保證提供服務。

計數器

計數器採用簡單的計數操作,到一段時間節點後自動清零

package cache;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

public class Counter {
	public static void main(String[] args) {
		//計數器,這裡用訊號量實現
		final Semaphore semaphore = new Semaphore(3);
		
		ScheduledExecutorService service = Executors.newScheduledThreadPool(1);
		service.scheduleAtFixedRate(new Runnable() {
			@Override
			public void run() {
				semaphore.release(3);
			}
		}, 3000, 3000, TimeUnit.MILLISECONDS);
		
		
		//模擬無數個請求從天而降
		while(true) {
			try {
				semaphore.acquire();
			}catch(InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println("ok");
		}
	}
}

優缺點

  • 實現起來非常簡單。
  • 控制力度太過於簡略,假如1s內限制3次,那麼如果3次在前100ms內已經用完,後面的900ms將只能處於阻
    塞狀態,白白浪費掉。

使用計數器限流的場景較少,因為它的處理邏輯不夠靈活。最常見的可能在web的登入密碼驗證,輸入錯誤次數凍結一段時間的場景。如果網站請求使用計數器,那麼惡意攻擊者前100ms吃掉流量計數,使得後續正常的請求被全部阻斷,整個服務很容易被搞垮。

漏桶演算法

漏桶演算法將請求快取在桶中,服務流程勻速處理。超出桶容量的部分丟棄。漏桶演算法主要用於保護內部的處理業務,保障其穩定有節奏的處理請求,但是無法根據流量的波動彈性調整響應能力。現實中,類似容納人數有限的服務大廳開啟了固定的服務視窗。

package cache;

import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class Barrel {
	public static void main(String[] args) {
		//桶,用阻塞佇列實現,容量為3
		final LinkedBlockingQueue<Integer> que = new LinkedBlockingQueue<>(3);
		
		//定時器,相當於服務的視窗,2s處理一個
		ScheduledExecutorService service = Executors.newScheduledThreadPool(1);
		service.scheduleAtFixedRate(new Runnable() {
			@Override
			public void run() {
				int v = que.poll();
				System.out.println("處理:"+v);
			}
		}, 2000, 2000, TimeUnit.MILLISECONDS);
		
		int i = 0;
		while(true) {
			i++;
			try {
				System.out.println("put:"+i);
				//如果是put,會一直等待桶中有空閒位置,不會丟棄
				//que.put(i);
				//等待1s如果進不了桶,就溢位丟棄
				que.offer(i,1000,TimeUnit.MILLISECONDS);
			}catch(Exception e) {
				e.printStackTrace();
			}
		}
	}
}

優缺點

  • 有效的擋住了外部的請求,保護了內部的服務不會過載
  • 內部服務勻速執行,無法應對流量洪峰,無法做到彈性處理突發任務
  • 任務超時溢位時被丟棄。現實中可能需要快取佇列輔助保持一段時間

應用

nginx中的限流是漏桶演算法的典型應用,配置案例如下:

http{
#$binary_remote_addr表示通過remote_addr這個標識來做key,也就是限制同一客戶端ip地址。
	#zone=one:10m表示生成一個大小為10M,名字為one的記憶體區域,用來儲存訪問的頻次資訊。 
	#rate=1r/s表示允許相同標識的客戶端每秒1次訪問 
limit_req_zone$binary_remote_addrzone=one:10mrate=1r/s;
server{
location/limited/{
#zone=one與上面limit_req_zone裡的name對應。
		#burst=5緩衝區,超過了訪問頻次限制的請求可以先放到這個緩衝區內,類似程式碼中的佇列長度。
		#nodelay如果設定,超過訪問頻次而且緩衝區也滿了的時候就會直接返回503,如果沒有設定,則所有請求會等待排隊,類似程式碼中的put還是offer。
limit_reqzone=oneburst=5nodelay;
}
}

令牌桶

令牌桶演算法可以認為是漏桶演算法的一種升級,它不但可以將流量做一步限制,還可以解決漏桶中無法彈性伸縮處理請求的問題。體現在現實中,類似服務大廳的門口設定門禁卡發放。發放是勻速的,請求較少時,令牌可以快取起來,供流量爆發時一次性批量獲取使用。而內部服務視窗不設限。

package cache;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

public class Token {

	public static void main(String[] args) throws InterruptedException {
		//令牌桶,訊號量實現,容量為3
		final Semaphore semaphore = new Semaphore(3);
		
		//定時器,1s一個,勻速頒發令牌
		ScheduledExecutorService service = Executors.newScheduledThreadPool(1);
		service.scheduleAtFixedRate(new Runnable() {
			@Override
			public void run() {
				if(semaphore.availablePermits() < 3) {
					semaphore.release();
				}
				System.out.println("令牌數:"+semaphore.availablePermits());
			}
		}, 1000, 1000, TimeUnit.MILLISECONDS);
		
		//等待,等候令牌桶儲存
		Thread.sleep(5);
		
		//模擬洪峰5個請求,前3個迅速響應,後兩個排隊
		for (int i = 0; i < 5; i++) {
			semaphore.acquire();
			System.out.println("洪峰:"+i);
		}
		
		//模擬日常請求,2s一個
		for (int i = 0; i < 3; i++) {
			Thread.sleep(1000);
			semaphore.acquire();
			System.out.println("日常:"+i);
			Thread.sleep(1000);
		}
		
		//再次洪峰
		for (int i = 0; i < 5; i++) {
			semaphore.acquire();
			System.out.println("洪峰:"+i);
		}
		
		//檢查令牌桶的數量
		for (int i = 0; i < 5; i++) {
			Thread.sleep(2000);
			System.out.println("令牌剩餘:"+semaphore.availablePermits());
		}
	}
}

執行結果:

洪峰0-2迅速被執行,說明桶中暫存了3個令牌,有效應對了洪峰
洪峰3,4被間隔性執行,得到了有效的限流
日常請求被勻速執行,間隔均勻
第二波洪峰來臨,和第一次一樣
請求過去後,令牌最終被均勻頒發,積累到3個後不再上升

應用

springcloudgateway可以配置令牌桶實現限流控制,案例如下:

cloud:
gateway:
routes:
‐id:limit_route
uri:http://localhost:8080/test
filters:
‐name:RequestRateLimiter
args:
#限流的key,ipKeyResolver為spring中託管的Bean,需要擴充套件KeyResolver介面 
key‐resolver:'#{@ipResolver}'
#令牌桶每秒填充平均速率,相當於程式碼中的發放頻率
redis‐rate‐limiter.replenishRate:1
#令牌桶總容量,相當於程式碼中,訊號量的容量
redis‐rate‐limiter.burstCapacity:3

滑動視窗

滑動視窗可以理解為細分之後的計數器,計數器粗暴的限定1分鐘內的訪問次數,而滑動視窗限流將1分鐘拆為多個段,不但要求整個1分鐘內請求數小於上限,而且要求每個片段請求數也要小於上限。相當於將原來的計數週期做了多個片段拆分。更為精細。

實現:

package cache;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

public class Token {

	public static void main(String[] args) throws InterruptedException {
		//令牌桶,訊號量實現,容量為3
		final Semaphore semaphore = new Semaphore(3);
		
		//定時器,1s一個,勻速頒發令牌
		ScheduledExecutorService service = Executors.newScheduledThreadPool(1);
		service.scheduleAtFixedRate(new Runnable() {
			@Override
			public void run() {
				if(semaphore.availablePermits() < 3) {
					semaphore.release();
				}
				System.out.println("令牌數:"+semaphore.availablePermits());
			}
		}, 1000, 1000, TimeUnit.MILLISECONDS);
		
		//等待,等候令牌桶儲存
		Thread.sleep(5);
		
		//模擬洪峰5個請求,前3個迅速響應,後兩個排隊
		for (int i = 0; i < 5; i++) {
			semaphore.acquire();
			System.out.println("洪峰:"+i);
		}
		
		//模擬日常請求,2s一個
		for (int i = 0; i < 3; i++) {
			Thread.sleep(1000);
			semaphore.acquire();
			System.out.println("日常:"+i);
			Thread.sleep(1000);
		}
		
		//再次洪峰
		for (int i = 0; i < 5; i++) {
			semaphore.acquire();
			System.out.println("洪峰:"+i);
		}
		
		//檢查令牌桶的數量
		for (int i = 0; i < 5; i++) {
			Thread.sleep(2000);
			System.out.println("令牌剩餘:"+semaphore.availablePermits());
		}
	}
}

模擬零零散散的請求,會造成每個片裡均有計數,總數達到上限後,不再響應,限流生效

再模擬突發的流量請求,會造成單片流量計數達到上限,不再響應而被限流

應用

滑動視窗演算法,在tcp協議發包過程中被使用。在web現實場景中,可以將流量控制做更細化處理,解決計數器模型控制力度太粗暴的問題。

每個人都有潛在的能量,只是很容易被習慣所掩蓋,被時間所迷離,被惰性所消磨~