<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          《餐廳小故事》| 服務限流的實施

          共 5222字,需瀏覽 11分鐘

           ·

          2021-08-03 07:22

          大家好,我是小菜。一個希望能夠成為 吹著牛X談架構 的男人!如果你也想成為我想成為的人,不然點個關注做個伴,讓小菜不再孤單!

          本文主要介紹 服務限流

          如有需要,可以參考

          如有幫助,不忘 點贊 ?

          微信公眾號已開啟,小菜良記,沒關注的同學們記得關注哦!

          天氣微涼,適合火鍋?走起!小菜來到了海上撈火鍋店,意料之中的人滿為患,想走?嘴巴卻不同意。那只能拿號排隊了!看著每家店都是人滿為患的樣子,心里不禁意想了起來,如果我是某家店的老板那還用的敲代碼嗎?

          瞎想總會成為可能,我不禁洋洋為我將來的餐廳考慮起來了,一家飯店,受場地規(guī)模和工作人員的因素能夠承載的客流總是被限制的,因此很多受歡迎的餐廳在高峰期的時候都需要排隊,餐廳為滿載之后的客人排號,只有當顧客用餐完畢之后,才能讓有號碼牌且對應號碼順序的顧客進入餐廳就餐。那為什么要這么設計呢?其實這就是一種限流措施,嚴格控制客流量使其穩(wěn)定在餐廳的運營能力之內,不會因客流量驟增而導致餐廳無法正常營業(yè)。這種限流方式保證在餐廳內就餐的顧客總數(shù)(并發(fā)量)是一致的。只有走了一個客人,才能允許一位客人進入,井條有序,合理運行!

          服務限流 應當是每個并發(fā)程序都應該考慮的~!限流的目的不僅是為了控制訪問的總并發(fā)量,而且還要盡量讓訪問的流量來的更均衡,這樣才不會讓系統(tǒng)的負載大起大落,因此又稱為"流量整形"

          當然在微服務盛行的時代,我們考慮到的 服務限流 不再單單應對 單體服務 ,而是更要清楚分布式場景下如何進行 服限流

          一丶單體限流

          image-20210730215659302

          以上三種是我們在 單體服務 中常見的限流算法,我們接下來分別認識一下!

          1、計數(shù)器限流

          計數(shù)器限流 是屬于一種比較簡單粗暴的方式!

          設計思路如下:

          我們會限制一秒鐘內能夠通過的請求數(shù)(比如 50),從第一個請求進來開始計數(shù),在接下去的1s 內,每進來一個請求,我們就會把計數(shù)值加 1,如果累加的數(shù)字達到了 50,那么后續(xù)的請求就會被全部拒絕,等到 1s  過去之后,把計數(shù)恢復成 0,并重新開始計數(shù)

          使用計數(shù)器可以用來限制一定時間內的總并發(fā)數(shù),但說到底這是一種簡單粗暴的限流方式,而不是平均速率限流,在某些場景下可以使用。但是遇到某些特殊的情況下,如果系統(tǒng)的負載量只有 50,在第59秒瞬間請求 50 次,并且在第 1:00 也請求了 50 次,那么這個程序在 1 秒內被請求了 100次,瞬間超過總負載,很有可能直接擊垮我們的應用程序!

          當然,事情都沒有絕對的,我們可以使用 滑動窗口 的方式解決問題。說到 滑動窗口 有些小伙伴并不陌生,因為 TCP 協(xié)議 就有采用 滑動窗口來控制流量,不清楚的小伙伴往下看!

          滑動窗口算法指的是以當前時間為截止時間,往前取一定的時間,比如取 60 秒時間,在這 60 秒之內運行的最大訪問數(shù)為 50,此時算法的執(zhí)行邏輯為:先清除 60 秒之前的所有請求記錄,再計算當前集合內請求數(shù)量是否大于設定的最大請求數(shù) 50?如果大于則執(zhí)行限流拒絕策略,否則插入本次請求記錄并執(zhí)行正常流程。

          我們在上圖可以看出,一個被紅色線段圈起來的就可以認為是一個時間窗口(1分鐘) ,然后我們將時間窗口進行劃分為 5 小格子,也就相當于 1 個小格是 12 s,每超過 12 s ,時間窗口就會往前步移一格,每一格都有自己獨立的計數(shù)器,假設第 35 秒的時候來了一個請求,那么 0:25~0:36 這個范圍格子的計數(shù)值就會加 1 。

          我們通過上圖回顧下 計數(shù)器 限流會遇到的問題,當 0:59 來了 50 個請求時會落在上圖紫色區(qū)域 中,如果 1:00 又來了 50 個請求,會落在上圖的 粉色區(qū)域 中,因為時間窗口的移動,總共 100 個請求落在了同一個時間窗口中,就會被檢測出從而觸發(fā)限流。而這就是 滑動窗口 的思想,接下來我們可以借助 Redis 來簡單演示下:

          執(zhí)行結果:

          Thread-0 正常執(zhí)行
          Thread-2 正常執(zhí)行
          Thread-3 正常執(zhí)行
          Thread-6 正常執(zhí)行
          Thread-7 正常執(zhí)行
          Thread-10 正常執(zhí)行
          Thread-11 正常執(zhí)行
          Thread-14 正常執(zhí)行
          Thread-1 正常執(zhí)行
          Thread-4 正常執(zhí)行
          Thread-5 正常執(zhí)行
          Thread-8 超出最大的系統(tǒng)負載量, 執(zhí)行限流
          Thread-8 正常執(zhí)行
          Thread-9 超出最大的系統(tǒng)負載量, 執(zhí)行限流
          Thread-9 正常執(zhí)行
          Thread-12 超出最大的系統(tǒng)負載量, 執(zhí)行限流
          Thread-12 正常執(zhí)行
          Thread-13 超出最大的系統(tǒng)負載量, 執(zhí)行限流
          Thread-13 正常執(zhí)行

          這段簡易的代碼當然有許多漏洞,但是僅僅給你提供一個實現(xiàn)的思路!

          2、漏桶算法

          我們開頭說到的 餐廳排號 其實就是一種類漏桶的實現(xiàn)方式,餐廳的容量就相當于是一個 桶容量,桶的容量是固定的,桶底的水會不斷的流出(用餐結束的顧客),桶頂?shù)乃?strong style="color: rgb(37, 132, 181);">待用餐的顧客)不斷流入。如果流入的水量(請求量)超出了流出的桶流量(最大并發(fā)量),桶滿后新流入的水會直接溢出,這就是限流應用中常用的漏桶算法。

          其實 Java 就已經自帶了一個很好實現(xiàn)漏桶算法的工具,那就是 Semaphore,它可以有效的控制服務的最大并發(fā)總數(shù),防止服務過載。下面是 Semaphore 的典型用法:

          通過上述例子我們不難發(fā)現(xiàn),漏桶算法主要關注的是當前的并發(fā)總量(信號總量),只有某個資源被釋放的信號發(fā)出(release操作),等待進入的請求才能獲得“通行證”,有出才有進,我們通過這種方式同樣可以保證系統(tǒng)的負載可控。

          3、令牌桶算法

          限流的另一種常用算法是令牌桶算法,它的實現(xiàn)原理為系統(tǒng)以恒定的速度往桶中放入令牌,請求需要從桶中獲取令牌才能被處理,一旦桶中無令牌可取,則拒絕服務。

          我們可以借助第三方工具實現(xiàn)該算法,如 Google Guava 的 RateLimiter組件則是采用令牌桶算法,以下是簡單的使用示例:

          OUTPUT:
          Thread-1 2021-08-01 00:09:14
          Thread-10 2021-08-01 00:09:14
          Thread-9 2021-08-01 00:09:15
          Thread-8 2021-08-01 00:09:15
          Thread-6 2021-08-01 00:09:16
          Thread-7 2021-08-01 00:09:16
          Thread-5 2021-08-01 00:09:17
          Thread-3 2021-08-01 00:09:17
          Thread-4 2021-08-01 00:09:18
          Thread-2 2021-08-01 00:09:18

          從上面的結果上來看,令牌確實是 1 秒產生 2 個,而 acquire()  方法為阻塞等待令牌,它可以傳遞一個 int 類型的參數(shù),用來指定獲取令牌的個數(shù),當然它還有一種替代方法 tryAcquire(),此方法在沒有可用令牌的時候就會直接返回 false ,這樣就不會阻塞等待了。當然 tryAcquire()可以設置超時事件,未超過最大等待事件會阻塞等待獲取令牌,如果超過了最大等待時間還沒有可用的令牌就會返回 false

          OUTPUT:
          limit
          limit
          limit
          limit
          limit
          limit
          limit
          limit
          Thread-10 2021-08-01 00:08:05
          Thread-4 2021-08-01 00:08:05

          通過以上例子我們可以總結:使用 RateLimiter實現(xiàn)的令牌桶算法不僅可以應對正常流量的限速,而且可以處理突發(fā)暴增的請求,實現(xiàn)平滑限流。

          二丶分布式限流

          單機限流場景下,各個服務節(jié)點負責各自機器的限流,不關注其他節(jié)點,更不關注集群的總調用量~!但是后臺資源是有限的,在分布式的場景下,我們的關注點不能再集中于某個節(jié)點上,有時候雖然各個單節(jié)點的流量都沒有超,但是各個節(jié)點的流量和卻超過了后臺資源的總承受量,所以必須控制服務在所有節(jié)點上的總流量,這就是 分布式限流。

          說到分布式,我們會想到 網關 的概念

          相關閱讀請空降:《吃透微服務》 - 服務網關之Gateway

          當我們在了解完網關的概念與作用后,自然清楚總流量控制可以在網關層面進行限流,但是有種 P2P 直連模式的服務集群就沒有網關的概念。這個時候要怎么辦呢?

          我們上面說到有時候單個節(jié)點沒有超過總流量,但是節(jié)點流量和卻超過了總流量。那我們不妨先匯總每個服務節(jié)點的流量,并將匯總后的流量與預設的總流量進行比較,如果超過了總流量就需要進行限流。

          簡單來說就是我們如果集群的承載量為 1000,但是匯總出來的總流量是 1200,這個時候超了 200,就需要進行限流!那我們這個時候就需要把流量降低到 ( 1 - (200/1200) ) = 0.83,而這個 0.83 就是個單機閾值,也就是我們的限流比例,每個節(jié)點都要將當前的流量降低這個比例。

          通過限流比例算出各自的限流閾值,然后再根據(jù)各自的限流閾值去調用上面說到的單機限流幾種算法去做單機限流。因此集群環(huán)境下的限流也是以單點的限流為基礎,但是在流量判定上有所不同。上面說的是一種限流思路的方向,接下來說下兩種具體的限流操作。

          1、Redis + Lua

          這個限流策略重點在于 Lua 腳本的編寫,什么是 Lua 腳本?有些同學又不淡定了~了解分布式鎖的同學應該清楚可以利用 Redis + Lua  實現(xiàn)分布式鎖。

          Lua是一種輕量小巧的腳本語言,用標準C語言編寫并以源代碼形式開放,其設計目的是為了嵌入應用程序中,從而為程序提供靈活的擴展和定制功能。

          既然 Lua 是核心,那我們先理清一下 Lua的邏輯實現(xiàn):

          看著流程圖自然而然代碼就出來了~

          1、首先定義兩個全局變量分別用來接收 Redis 應用中傳遞的鍵和限流大小

          2、在應用端傳遞 KEYS  是一個數(shù)據(jù)列表,在 Lua 腳本中通過索引下標方式獲取數(shù)組內的值

          3、在應用端傳遞 ARGV 參數(shù)比較靈活,可以是一個或多個獨立的參數(shù),但對應到 Lua 腳本中統(tǒng)一使用 ARGV 這個數(shù)組接收,獲取方式也是通過數(shù)組下標獲取

          編輯好 Lua 腳本后,我們就可以在 Java 中愉快的使用了:

          在具體的業(yè)務場景中,我們可以自定義一個限流注解,配合 AOP  切面達到限流的效果!

          2、Nginx + Lua

          使用 Nginx + Lua 的方式對系統(tǒng)侵入性較低!我們直接看代碼

          Lua部分

          可以參考 OpenResty 官方給出的限流示例,稍加修改即可

          然后我們需要修改 nginx.conf 配置文件:

          http 塊中添加

          image-20210801225750137

          然后在需要限流的 server 塊中添加:

          當然除了以上說到的兩種實現(xiàn)思路外,我們還可以利用現(xiàn)成的中間件 HystrixSentinel

          Sentinel 相關閱讀請空降:《吃透微服務》 - 服務容錯之Sentinel

          說到最后

          說完了兩種場景的限流,當然只是在很粗淺的層面泛泛而談!不妨再讓我以一段騷話結尾:我們在習慣單應用服務的時候,會發(fā)現(xiàn)其實單純的單點限流并不難,因為我們的關注點是 1 ,由 1 進行延伸,事態(tài)往往會變得不可控,如果說單應用服務的時候我們還有很多現(xiàn)成的組件可以選擇,但是如果要考慮到整個分布式集群的限流方式,我們往往不知所措。我們需要考慮服務節(jié)點的調用監(jiān)控,日志采集,日志聚合,計算分析,限流決策判斷等等許多環(huán)節(jié),但是不必恐懼,我們換個方向想想,當你考慮的越多,不正說明你提升也會越多,最怕的不是困難與挑戰(zhàn),而是你原地踏步不前的無知!

          我是小菜,與你結伴而行~

          不要空談,不要貪懶,和小菜一起做個吹著牛X做架構的程序猿吧~點個關注做個伴,讓小菜不再孤單。咱們下文見!

          看完不贊,都是壞蛋

          今天的你多努力一點,明天的你就能少說一句求人的話!
          我是小菜,一個和你一起變強的男人。 ??
          微信公眾號已開啟,小菜良記,沒關注的同學們記得關注哦!


          瀏覽 54
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲一卡二卡三卡四卡五卡 | 夜夜国自一区 1080P | 日韩一级片在线播放 | aaaaaa在线 | 欧美精品A |