<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>

          SpringCloud微服務(wù)網(wǎng)關(guān)做邊緣服務(wù)限流方案

          共 5735字,需瀏覽 12分鐘

           ·

          2020-11-19 18:30

          點擊上方藍色字體,選擇“標(biāo)星公眾號”

          優(yōu)質(zhì)文章,第一時間送達

          66套java從入門到精通實戰(zhàn)課程分享

          在高并發(fā)的系統(tǒng)中,往往需要在系統(tǒng)中做限流,一方面是為了防止大量的請求使服務(wù)器過載,導(dǎo)致服務(wù)不可用,另一方面是為了防止網(wǎng)絡(luò)攻擊。?

          常見的限流方式,比如Hystrix適用線程池隔離,超過線程池的負載,走熔斷的邏輯。在一般應(yīng)用服務(wù)器中,比如tomcat容器也是通過限制它的線程數(shù)來控制并發(fā)的;也有通過時間窗口的平均速度來控制流量。常見的限流緯度有比如通過Ip來限流、通過uri來限流、通過用戶訪問頻次來限流。

          一般限流都是在網(wǎng)關(guān)這一層做,比如Nginx、Openresty、kong、zuul、Spring Cloud Gateway等;也可以在應(yīng)用層通過Aop這種方式去做限流。這里主要講講常用的限流方式。
          一、常見的限流算法
          目前常用的限流算法有兩個:漏桶算法和令牌桶算法。
          1·計數(shù)器算法
          計數(shù)器算法采用計數(shù)器實現(xiàn)限流有點簡單粗暴,一般我們會限制一秒鐘的能夠通過的請求數(shù),比如限流qps為100,算法的實現(xiàn)思路就是從第一個請求進來開始計時,在接下去的1s內(nèi),每來一個請求,就把計數(shù)加1,如果累加的數(shù)字達到了100,那么后續(xù)的請求就會被全部拒絕。等到1s結(jié)束后,把計數(shù)恢復(fù)成0,重新開始計數(shù)。具體的實現(xiàn)可以是這樣的:對于每次服務(wù)調(diào)用,可以通過AtomicLong#incrementAndGet()方法來給計數(shù)器加1并返回最新值,通過這個最新值和閾值進行比較。這種實現(xiàn)方式,相信大家都知道有一個弊端:如果我在單位時間1s內(nèi)的前10ms,已經(jīng)通過了100個請求,那后面的990ms,只能眼巴巴的把請求拒絕,我們把這種現(xiàn)象稱為“突刺現(xiàn)象”
          2.漏桶算法
          漏桶算法的原理比較簡單,請求進入到漏桶中,漏桶以一定的速率漏水。當(dāng)請求過多時,水直接溢出??梢钥闯?,漏桶算法可以強制限制數(shù)據(jù)的傳輸速度。但高并發(fā)情況下最容易出現(xiàn)瓶頸。

          3.令牌桶算法
          令牌桶算法的原理是系統(tǒng)以一定速率向桶中放入令牌,如果有請求時,請求會從桶中取出令牌,如果能取到令牌,則可以繼續(xù)完成請求,否則等待或者拒絕服務(wù)。這種算法可以應(yīng)對突發(fā)程序的請求,因此比漏桶算法好。

          在Wikipedia上,令牌桶算法是這么描述的:

          ·每秒會有r個令牌放入桶中,或者說,每過1/r 秒桶中增加一個令牌
          ·桶中最多存放b個令牌,如果桶滿了,新放入的令牌會被丟棄
          ·當(dāng)一個n字節(jié)的數(shù)據(jù)包到達時,消耗n個令牌,然后發(fā)送該數(shù)據(jù)包
          ·如果桶中可用令牌小于n,則該數(shù)據(jù)包將被緩存或丟棄
          Spring Cloud Gateway限流
          在Spring Cloud Gateway中,有Filter過濾器,因此可以在“pre”類型的Filter中自行實現(xiàn)上述三種過濾器。但是限流作為網(wǎng)關(guān)最基本的功能,Spring Cloud Gateway官方就提供了RequestRateLimiterGatewayFilterFactory這個類,適用Redis和lua腳本實現(xiàn)了令牌桶的方式。具體實現(xiàn)邏輯在RequestRateLimiterGatewayFilterFactory類中,lua腳本在如下圖所示的文件夾中:

          讀者可以自行查看,先以案例的形式來講解如何在Spring Cloud Gateway中使用內(nèi)置的限流過濾器工廠來實現(xiàn)限流。

          首先在工程的pom文件中引入gateway的起步依賴和redis的reactive依賴,代碼如下:

          ?
          ????org.springframework.cloud
          ????spring-cloud-starter-gateway



          ????org.springframework.boot
          ????spring-boot-starter-data-redis-reactive



          在配置文件中做以下的配置:


          server:
          ??port:?8088
          spring:
          ??cloud:
          ????gateway:
          ??????routes:
          ??????-?id:?limit_route
          ????????uri:?http://httpbin.org.com:80/get
          ????????predicates:
          ????????-?After=2017-01-20T17:42:47.789-07:00[America/Denver]
          ????????filters:
          ????????-?name:?RequestRateLimiter
          ??????????args:
          ????????????key-resolver:?'#{@hostAddrKeyResolver}'
          ????????????redis-rate-limiter.replenishRate:?1
          ????????????redis-rate-limiter.burstCapacity:?3
          ??application:
          ????name:?gateway-limiter
          ??redis:
          ????host:?localhost
          ????port:?6379
          ????database:?0

          在上面的配置文件,指定程序的端口為8088,配置了 redis的信息,并配置了RequestRateLimiter的限流過濾器,該過濾器需要配置三個參數(shù):

          burstCapacity,令牌桶總?cè)萘俊?br style="outline: 0px;">replenishRate,令牌桶每秒填充平均速率。
          key-resolver,用于限流的鍵的解析器的 Bean 對象的名字。它使用 SpEL 表達式根據(jù)#{@beanName}從 Spring 容器中獲取 Bean 對象。
          KeyResolver需要實現(xiàn)resolve方法,比如根據(jù)Hostname進行限流,則需要用hostAddress去判斷。實現(xiàn)完KeyResolver之后,需要將這個類的Bean注冊到Ioc容器中。

          public class HostAddrKeyResolver implements KeyResolver {

          @Override
          public?Mono?resolve(ServerWebExchange?exchange)?{
          ????return?Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());
          }

          }

          @Bean
          public HostAddrKeyResolver hostAddrKeyResolver() {
          return new HostAddrKeyResolver();
          }

          可以根據(jù)uri去限流,這時KeyResolver代碼如下:

          public class UriKeyResolver implements KeyResolver {

          @Override
          public?Mono?resolve(ServerWebExchange?exchange)?{
          ????return?Mono.just(exchange.getRequest().getURI().getPath());
          }

          }

          @Bean
          public UriKeyResolver uriKeyResolver() {
          return new UriKeyResolver();
          }

          也可以以用戶的維度去限流:

          @Bean
          KeyResolver userKeyResolver() {
          return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst(“user”));
          }

          用jmeter進行壓測,配置10thread去循環(huán)請求lcoalhost:8088,循環(huán)間隔1s。從壓測的結(jié)果上看到有部分請求通過,由部分請求失敗。通過redis客戶端去查看redis中存在的key。

          可見,RequestRateLimiter是使用Redis來進行限流的,并在redis中存儲了2個key。關(guān)注這兩個key含義可以看lua源代碼。
          使用Guava的RateLimiter做限流
          Guava中開源出來一個令牌桶算法的工具類RateLimiter,可以輕松實現(xiàn)限流的工作。RateLimiter對簡單的令牌桶算法做了一些工程上的優(yōu)化,具體的實現(xiàn)是SmoothBursty。需要注意的是,RateLimiter的另一個實現(xiàn)SmoothWarmingUp,就不是令牌桶了,而是漏桶算法。也許是出于簡單起見,RateLimiter中的時間窗口能且僅能為1S,如果想搞其他時間單位的限流,只能另外造輪子。

          RateLimiter有一個有趣的特性是[前人挖坑后人跳],也就是說RateLimiter允許某次請求拿走了超出剩余令牌數(shù)的令牌,但是下一次請求將為此付出代價,一直等到令牌虧空補上,并且桶中有足夠本次請求使用的令牌為止。這里面就涉及到一個權(quán)衡,是讓前一次請求干等到令牌夠用才走掉呢,還是讓它走掉后面的請求等一等呢?Guava的設(shè)計者選擇的是后者,先把眼前的活干了,后面的事后面再說。

          測試代碼:

          public?class?RateLimiterMain?{
          ????public?static?void?main(String[]?args)?{
          ????????RateLimiter?rateLimiter?=?RateLimiter.create(2);
          ????????System.out.println(rateLimiter.acquire(5));
          ????????System.out.println(rateLimiter.acquire(2));
          ????????System.out.println(rateLimiter.acquire(1));
          ????}
          }


          輸出內(nèi)容:

          0.0
          2.496889
          0.992149
          可以看出,令牌桶每秒只能產(chǎn)生2個令牌,我們可以第一次取出5個,但是第二次再去取令牌的時候,需要等2.5s,也就是第一次令牌取完后,需要等2.5s才能取到令牌。同樣的,第三次取1個令牌的時候,也需要等待第二次的1s的時間。也就是,取的速率可以超過令牌產(chǎn)生的速率,但是下一次再次去取的時候,需要阻塞等待。

          當(dāng)然也可以使用tryAcquire來非阻塞的獲取,可以實時返回結(jié)果。另外tryAcquire也可以傳入?yún)?shù),也就是等待的時間,超時直接返回false。這點等同于常見的lock,tryLock。
          并發(fā)控制Semapphore
          一般來說,在網(wǎng)關(guān)系統(tǒng)中,還有一個參數(shù)叫并發(fā)控制,就是某一個資源可以被同時訪問的個數(shù)。這種情況下,我們可以使用Semaphore來控制。

          Semaphore不同于互斥鎖?;コ怄i是某個資源只能支持同時一個訪問,而Semaphore可以支持多個訪問,但是加上了總數(shù)的控制。

          示例
          4.1 在pom中加入guava依賴


          ?com.google.guava
          ?guava
          ?18.0


          把限流服務(wù)封裝到一個類中LimitService,提供tryAcquire()方法,用來嘗試獲取令牌,返回true表示獲取到,如下所示:

          @Service
          public?class?LimitService?{

          ????//每秒只發(fā)出5個令牌
          ????RateLimiter?rateLimiter?=?RateLimiter.create(5.0);

          ????/**
          ?????*?嘗試獲取令牌
          ?????*?@return
          ?????*/
          ????public?boolean?tryAcquire(){
          ????????return?rateLimiter.tryAcquire();
          ????}
          }

          調(diào)用方是個普通的controller,每次收到請求的時候都嘗試去獲取令牌,獲取成功和失敗打印不同的信息,如下:

          @Controller
          public?class?HelloController?{

          ????private?static?SimpleDateFormat?sdf?=?new?SimpleDateFormat("yyyy-MM-dd?HH:mm:ss");

          ????@Autowired
          ????private?LimitService?limitService;

          ????@RequestMapping("/access")
          ????@ResponseBody
          ????public?String?access(){
          ????????//嘗試獲取令牌
          ????????if(limitService.tryAcquire()){
          ????????????//模擬業(yè)務(wù)執(zhí)行500毫秒
          ????????????try?{
          ????????????????Thread.sleep(500);
          ????????????}catch?(InterruptedException?e){
          ????????????????e.printStackTrace();
          ????????????}
          ????????????return?"aceess?success?["?+?sdf.format(new?Date())?+?"]";
          ????????}else{
          ????????????return?"aceess?limit?["?+?sdf.format(new?Date())?+?"]";
          ????????}
          ????}
          }





          版權(quán)聲明:本文為博主原創(chuàng)文章,遵循 CC 4.0 BY-SA 版權(quán)協(xié)議,轉(zhuǎn)載請附上原文出處鏈接和本聲明。

          本文鏈接:

          https://blog.csdn.net/weixin_39654286/article/details/109688116






          粉絲福利:實戰(zhàn)springboot+CAS單點登錄系統(tǒng)視頻教程免費領(lǐng)取

          ???

          ?長按上方微信二維碼?2 秒
          即可獲取資料



          感謝點贊支持下哈?

          瀏覽 48
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

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

          手機掃一掃分享

          分享
          舉報
          <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>
                  jiZZjiZZ丰满熟妇 | 美日韩A片 | 欧美19p | 四虎影院毛片 | 无码精品黑人 |