日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【限流01】限流算法理论篇

發布時間:2024/3/24 编程问答 45 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【限流01】限流算法理论篇 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

微服務就是將復雜的大應用拆分成小的應用,這樣做的好處是各個應用之間獨立開發、測試、上線,互不影響。但是服務拆分之后,帶來的問題也很多,我們需要保障微服務的正常運行,就需要進行服務治理。常用手段有:鑒權、限流、降級、熔斷等。

其中,限流是指對某個接口的調用頻率進行限制,防止接口調用頻率過快導致線程資源被耗盡從而導致整個系統響應變慢。限流在很多業務中都有應用,比如:秒殺、雙11。當用戶請求量過大時,就會拒絕后續接口請求,保障系統的穩定性。

接口限流的實現思路是:統計某個時間段內的接口調用次數,當調用次數超過設置的閾值時,就進行限流限制接口訪問。

常見的限流算法有:固定時間窗口算法、滑動時間窗口算法、令牌桶算法、漏桶算法等,下面我們將一一介紹每種算法的實現思路和代碼實現。

一、固定時間窗口限流算法

1、算法概述

固定時間窗口限流算法的思路就是:確定一段時間段,在該時間段內統計接口的調用次數,來判斷是否限流。

實現步驟如下:
選定一個時間起點,當接口請求到來時,

  • 接口訪問次數小于閾值,可以訪問,接口訪問次數 + 1;
  • 接口訪問次數大于閾值,拒絕該時間段內后續訪問進行限流,接口訪問次數不變;
  • 進入下一個時間窗口之后,計數器清零,時間起點設置為當前時間,這樣就進入下一個時間窗口。

示意圖如下:

? (圖片來源:https://time.geekbang.org/column/article/80388?utm_term=zeusNGLWQ&utm_source=xiangqingye&utm_medium=geektime&utm_campaign=end&utm_content=xiangqingyelink1104,下圖同上)

這種限流算法的缺點是:無法應對兩個時間窗口臨界時間內的突發流量。

如下圖:假設要求每秒鐘接口請求次數不超過100,在第1s時間窗口內接口請求次數為100,但是都集中在最后10ms;第2s時間窗口內接口請求次數也為100,都集中在前10ms內;兩個時間窗口請求次數都小于100,滿足要求。但是在兩個10ms內接口請求次數=200 > 100。如果這個次數不是200,是2000萬,可能就會導致系統崩潰。

2、代碼實現

public class FixedWindowRateLimitAlg implements RateLimitAlg {// msprivate static final long LOCK_EXPIRE_TIME = 200L;private Stopwatch stopWatch;// 限流計數器private AtomicInteger counter = new AtomicInteger(0);private final int limit;private Lock lock = new ReentrantLock();public FixedWindowRateLimitAlg(int limit) {this(limit, Stopwatch.createStarted());}public FixedWindowRateLimitAlg(int limit, Stopwatch stopWatch) {this.limit = limit;this.stopWatch = stopWatch;}@Overridepublic boolean tryAcquire() throws InterruptedException {int currentCount = counter.incrementAndGet();// 未達到限流if (currentCount < limit) {return true;}// 使用固定時間窗口統計當前窗口請求數// 請求到來時,加鎖進行計數器統計工作try {if (lock.tryLock(LOCK_EXPIRE_TIME, TimeUnit.MILLISECONDS)) {// 如果超過這個時間窗口, 則計數器counter歸零, stopWatch, 窗口進入下一個窗口if (stopWatch.elapsed(TimeUnit.MILLISECONDS) > TimeUnit.SECONDS.toMillis(1)) {counter.set(0);stopWatch.reset();}// 不超過, 則當前時間窗口內的計數器counter+1currentCount = counter.incrementAndGet();return currentCount < limit;}} catch (InterruptedException e) {System.out.println("tryAcquire() wait lock too long:" + LOCK_EXPIRE_TIME + " ms");throw new InterruptedException("tryAcquire() wait lock too long:" + LOCK_EXPIRE_TIME + " ms");} finally {lock.unlock();}// 出現異常 不能影響接口正常請求return true;}}

二、滑動時間窗口限流算法

1、算法概述

固定時間窗口限流算法無法處理兩個時間窗口臨界值流量突增的情況。為了解決這個問題,我們可以稍微優化下固定時間窗口限流算法,通過限制任意時間窗口內(比如:1S)接口請求數都不超過某個閾值,這個優化后的算法就叫做滑動時間窗口限流算法。

滑動時間窗口限流算法將一個大的時間窗口分成粒度更小的時間窗口,每個子窗口獨立統計次數。每經過一個子窗口的時間,整體窗口就向右滑動一格。


如上圖所示,假設要求每分鐘通過次數不超過100次,將1分鐘分成6個10s的單元格。
第一個圖中假設最后1個10s內(序號:6)通過請求次數為100次,第二個圖中假設第1個10s (序號:7)內請求次數也通過100次。由于是滑動窗口,第一個窗口向右移動一格后,在第二個滑動窗口內,序號6、7兩者加起來的請求次數為200>100,所以限流,從而解決了固定時間窗口無法處理兩個窗口臨界值的問題。

雖然滑動時間窗口算法可以保證任意時間窗口內接口請求次數不超過閾值,但是仍然無法避免更細粒度流量突增的場景,比如在某個10s內的單元格內流量突增無法立即被限流。同時,使用滑動窗口算法時,流量曲線如下,無法達到平滑過渡的效果,無法控制流量速度

2、算法實現

可以使用循環隊列來實現滑動時間窗口限流算法:

假設限流規則是任意1s內,接口請求數不超過N次。

創建一個N+1 (循環隊列本身會浪費一個存儲單元,所以是N+1)的循環隊列,用來記錄1S內的請求。

當有新的請求到來時,

  • 將與該請求的時間間隔超過1s的請求從隊列中移除(移動head指針);
  • 再看循環隊列中是否有空閑位置,如果有,則把新請求存儲在隊列尾部(tail指針所在位置,同時移動tail指針);
  • 如果循環隊列尾部沒有空閑位置,說明這個1s內的請求次數已經超過限流次數N,拒絕后續服務。

算法實現的示意圖如下:

假設1S內請求次數不能超過6次,整個隊列分成(6+1)個單元格。

  • 18:060代表 18s 60ms的時候,第一個請求到來,此時隊列為空,于是存儲在第一個單元格內(即head指針指向的位置);
  • 同樣,18:123、18:336、18:569、18:702、18:906分別為第2、3、4、5、6個請求,均在1s間隔內且隊列都有空閑位置,于是依次存儲到對應單元格內;
  • 當19:003請求到來時,沒有與其超過1s間隔的請求,所以不需要移除其他請求;由于隊列尾部沒有空閑位置,說明1S內的請求次數已經超過6次,拒絕該請求訪問;
  • 當19:406到來時,與其超過1S間隔的請求有18:060、18:123、18:336,該3個請求需要移除隊列(逆時針移動head指針3個單元格),同時tail指針逆時針移動一格后,存放當前請求19:406;

3、偽代碼實現

/*** @author: wanggenshen* @date: 2020/6/29 21:56.* @description: 循環隊列實現滑動窗口限流算法*/ public class SlidingWindowLimiter {private int windowSize;private CircularQueue queue;public SlidingWindowLimiter(int windowSize) {this.windowSize = windowSize;queue = new CircularQueue(windowSize);}public boolean tryAcquire(long now) {// 判斷是否有間隔1s的請求, 有則移除隊列while (queue.prevNode() != -1 && now - queue.prevNode() > 1000) {System.out.println("超過1S間隔, 移除超過間隔的節點: " + queue.prevNode() + "當前時間: " + now + ", 間隔: " + (now - queue.prevNode()));queue.dequeue();}// 隊列已滿, 拒絕訪問if (queue.isFull()) {System.out.println("隊列已滿, now: " + now);return false;}queue.enqueue(now);return true;}static class CircularQueue {/*** 每次請求的時間戳*/private long[] timeQueue;/*** 隊列大小*/private int size;/*** 頭指針*/private int headIndex;/*** 尾指針*/private int tailIndex;public CircularQueue(int size) {// 循環隊列尾部指針多占用一個單元格timeQueue = new long[size + 1];this.size = size + 1;}/*** 入隊*/public void enqueue (long timestamp) {// 隊列已滿if (isFull()) {throw new RuntimeException("Exceed queue size.");}timeQueue[tailIndex] = timestamp;tailIndex = (tailIndex + 1) % size;}/*** 出隊** @return*/public long dequeue () {// 隊列為空if (isEmpty()) {return -1;}long timestamp = timeQueue[headIndex];headIndex = (headIndex + 1) % size;return timestamp;}public long prevNode() {// 隊列為空if (isEmpty()) {return -1;}return timeQueue[headIndex];}public boolean isFull() {return (tailIndex + 1) % size == headIndex;}public boolean isEmpty() {return tailIndex == headIndex;}}}

三、漏桶算法

1、算法概述

實際上,當請求數超過閾值時,我們不希望后續流量被全部限流,而是希望將流量控制在一定速度內。
漏桶算法就是基于流控來控制流量。

如下圖所示,調用方請求比作是水龍頭出的水,水桶出的水是比作是接口提供方處理的請求。當水龍頭出水速度大于桶里的水流出速度(類似接口調用請求頻率過快),水直接溢出(類似請求直接被限流)。通過這種方法不僅能保證流量不會超過閾值,同時保證接口的請求數以穩定的速度去處理。

漏桶算法的優點在于能夠控制接口提供方的接口被勻速處理;缺點在于設置的速率不當會影響接口處理的效率。

2、代碼實現

偽代碼如下:

/*** @author: wanggenshen* @date: 2020/6/29 21:00.* @description: 漏桶限流算法*/ public class LeakyBucketLimiter {/*** 桶內剩余的水*/private long left;/*** 桶的容量*/private long capacity;/*** 一桶水漏完的時間*/private long duration;/*** 桶漏水的速率, capacity = duration*velocity*/private double velocity;/*** 上一次成功放入水桶的時間*/private long lastUpdateTime;public boolean acquire() {long now = System.currentTimeMillis();// 剩余的水量 - 桶勻速漏出去的水left = Math.max(0, left - (long)((now - lastUpdateTime) * velocity));// 當前水桶再加一單位水沒有溢出, 則可以繼續訪問if (left++ <= capacity) {lastUpdateTime = now;return true;} else {return false;}} }

四、令牌桶算法

1、算法概述

令牌桶算法的實現原理是:

以恒定速率生成令牌放進令牌桶,令牌桶滿了的時候就丟棄不再放入令牌桶;

如果想要處理請求,就需要從令牌桶中取一個令牌。能取出令牌則去處理請求;沒有令牌則拒絕請求。

令牌桶算法與漏桶算法很類似,最主要的區別在于:

  • 漏桶算法輸入速率不定,但是輸出速率恒定;令牌桶算法輸出速率可以根據流量大小進行調整;

  • 從接口處理者的角度看,漏桶算法只能以固定頻率去處理請求(比如每秒只能處理1個請求,如果此時來了10個請求,漏桶需要花10s處理完);而令牌桶算法可以處理突發流量,比如來了20個請求,如果令牌桶中有>=20個令牌,那么處理者就可以一下子全部處理這20個請求;

2、偽代碼實現

/*** @author: wanggenshen* @date: 2020/6/29 21:00.* @description: 令牌桶限流算法*/ public class TokenBucketLimiter {/*** 令牌桶桶內剩余的令牌*/private long left;/*** 令牌桶的容量*/private long capacity;/*** 一桶水漏完的時間*/private long duration;/*** 令牌桶生產令牌的速率, capacity = duration*velocity*/private double velocity;/*** 上一次拿走令牌的時間*/private long lastUpdateTime;public boolean acquire() {long now = System.currentTimeMillis();// 令牌桶余量 = 【上一次令牌桶剩余的令牌】+ 【(上一次拿走令牌到現在的時間段) * 每個單位時間生產令牌的速率 】// 生產出的令牌 超過令牌桶的容量時, 則舍棄left = Math.min(capacity, left + (long)((now - lastUpdateTime) * velocity));// 若當前能夠成功領取令牌, 則可以訪問if (left-- >= 0) {lastUpdateTime = now;return true;} else {return false;}} }

生產環境下可以考慮使用Guava提供的令牌桶算法實現類: RateLimiter來進行限流,RateLimiter的實現是線程安全的。

五、分布式限流

生產環境下服務基本上分布式部署,那么在對服務進行限流時需要考慮到分布式限流。

最簡單的做法是給每臺應用服務器平均分配流控閾值,將分布式限流轉換為單機限流。如總流量不超過1000次,那么5個服務實例,每個實例請求數不能超過200次。但是如果遇到流量不均勻(比如一臺機器流量一直是10、另外幾臺> 200)、或者有一臺宕機,那么另外幾臺平均下來就是250>200,這種做法不是很好。

常見的實現思路有兩種:

  • 中心化:使用一個第三方服務統一存儲所有服務實例的調用次數,由其去判斷是否進行限流。這種方式需要注意第三方服務宕機導致不可用問題。這個時候可以退化成單機流控。
  • 去中心化:每個服務單獨保存同一份流控數據,但是很難做到保持狀態一致,即CAP中的C。

一般使用中心化這種思路。

1、TokenServer 流控

Sentinel提供了TokenServer,作為一個獨立服務來統計總調用量、判斷單個請求是否允許訪問。應用服務器每次接收到請求后,都要與TokenServer進行一次通信,判斷該次請求能否訪問。

這種實現方式的好處是:由TokenServer集中管理每個服務實例的總調用量,服務實例不用關心請求的統計工作;

缺點是:非常依賴于TokenServer的性能,因為需要與其進行網絡通信。同時需要關系TokenServer服務的單節點故障問題。

2、存儲式流控

存儲式流控是每個服務請求到來時,從第三方存儲(如Redis、MySQL)讀取接口請求數、然后再將請求數更新回緩存;

拿到請求數后由每個服務實例自己去判斷是否需要限流。

總結

要設計一個高性能、高可靠性的分布式流控性能需要考慮網絡通信、加鎖同步等對性能帶來的影響,同時也需要考慮分布式環境的可靠性。


參考:https://mp.weixin.qq.com/s/joP22Z8zblcDBAV1keSdJw

總結

以上是生活随笔為你收集整理的【限流01】限流算法理论篇的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 色欧美色 | 免费在线观看黄 | 黄色动漫在线观看 | 精品一区二区三区免费观看 | 日韩一级影视 | 热@国产 | 国产1区在线| 激情婷婷六月 | 最新中文字幕免费 | 午夜电影一区二区三区 | 国产美女三级无套内谢 | 日韩一区二区三 | 波多野结衣99 | 欧美大片在线观看 | 色吧综合| 一本一本久久a久久精品综合麻豆 | 美国av一区二区 | 国产男女爽爽爽 | 黄av在线播放 | 中文字幕免费高清在线 | 女生抠逼视频 | 亚洲AV无码成人国产精品色 | 在线观看成人 | 久久久综合色 | 国产污污 | 少妇淫片 | xxxxx69| 成人福利在线免费观看 | 中文字幕在线观看av | 91亚洲精 | 日韩字幕| 国产免费www | 久久久久久久久久久久97 | 国内精品毛片 | 蜜臀av免费在线观看 | 人人妻人人澡人人爽精品欧美一区 | 国产在线视频网 | 亚洲免费中文 | 二区三区av| 亚洲天堂2013 | 日本性生活一级片 | 精品久久久久久久久久久久久久久久久久 | 国产欧美日韩综合精品一区二区 | 日韩中文字幕在线观看 | 亚洲色图清纯唯美 | 免费国产黄色网址 | 欧美成人一级 | 欧美性生活网址 | 影音先锋人妻啪啪av资源网站 | 久久久久久久影院 | 国产精品亚洲а∨天堂免在线 | 国产日产精品一区二区三区 | 人妻无码一区二区三区免费 | 九九超碰 | 牛牛在线 | 三级黄在线观看 | 97精品人妻一区二区三区蜜桃 | 久草中文视频 | 欧美 中文字幕 | 95视频在线| 在线视频精品一区 | aaaa级片| 在线观看日韩av | 国产成人在线精品 | 亚洲午夜一区二区 | 男女操网站 | 777av| 欧美极品在线播放 | 中文字幕dvd| 99久久婷婷国产综合精品青牛牛 | 中文字幕一区二区三区久久久 | 久久精品视频18 | 手机av在线免费观看 | 综合在线观看 | 综合久久久久久 | 国产香蕉尹人视频在线 | 亚洲一级影片 | 精品一区在线播放 | 国产成人在线免费观看 | 九九久久国产精品 | 亚洲福利在线观看 | 亚洲女优在线 | 在线天堂中文字幕 | 麻豆国产精品777777在线 | 国产第一av| 看全色黄大色黄大片大学生 | 国产a级大片 | 一级毛片黄片 | 香蕉一级片 | 精品国产一区二区三区日日嗨 | 国产最新在线 | 午夜不卡视频 | 男人日女人免费视频 | 国精产品一二三区精华液 | www.欧美精品 | 天干夜天干天天天爽视频 | 高清免费视频日本 | 成人国产一区二区 | 强睡邻居人妻中文字幕 |