天天看點

dubbo熔斷限流限流

限流

  • 根據排隊理論,具有延遲的服務随着請求量的不斷提升,其平均響應時間也會迅速提升,為了保證服務的SLA(Service-Level Agreement 服務等級協定),有必要控制機關時間的請求量。這就是限流為什麼愈發重要的原因。

分類

  • qps限流
    限制每秒處理請求數不超過門檻值
  • 并發限流
    限制同時處理的請求數目。Java 中的 Semaphore(信号量) 是做并發限制的好工具,特别适用于資源有效的場景。
  • 單機限流
    Guava 中的 RateLimiter(内部采用令牌捅算法實作)。
  • 叢集限流
    redis限流,計時器和計數器處理、key加秒為key
    nginx(+lua)前端限流,按照一定的規則如帳号、IP、系統調用邏輯等在Nginx層面做限流

算法

常見的限流算法有:令牌桶、漏桶。計數器也可以進行粗暴限流實作。

  • 漏桶算法
    dubbo熔斷限流限流
  • 令牌桶算法
    dubbo熔斷限流限流

實施方案

RPC限流(dubbo)

dubbo調用模型

連接配接調用圖
dubbo熔斷限流限流
調用時關鍵參數影響
參數名 作用範圍 預設值 說明 備注
actives consumer 每服務消費者每服務每方法最大并發調用數 0表示不限制
connections consumer 對每個提供者的最大連接配接數,rmi、http、hessian等短連接配接協定表示限制連接配接數,dubbo等長連接配接協表示建立的長連接配接個數 dubbo時為1,即複用單連結
accepts provider 服務提供方最大可接受連接配接數 0表示不限制
iothreads provider cpu個數+1 io線程池大小(固定大小)
threads provider 200 業務線程池大小(固定大小)
executes provider 服務提供者每服務每方法最大可并行執行請求數 0表示不限制
tps provider 指定時間内(預設60s)最大的可執行次數,注意與executes的差別 預設不開啟
queues provider 線程池隊列大小,當線程池滿時,排隊等待執行的隊列大小,建議不要設定,當線程程池時應立即失敗,重試其它服務提供機器,而不是排隊,除非有特殊需求。
分析
  1. 當consumer發起一個請求時,首先經過active limit(參數actives)進行方法級别的限制,其實作方式為CHM中存放計數器(AtomicInteger),請求時加1,請求完成(包括常)減1,如果超過actives則等待有其他請求完成後重試或者逾時後失敗;
  2. 從多個連接配接(connections)中選擇一個連接配接發送資料,對于預設的netty實作來說,由于可以複用連接配接,預設一個連接配接就可以。不過如果你在壓測,且隻有一個consumer,一個provider,此時适當的加大connections确實能夠增強網絡傳輸能力。但線上業務由于有多個consumer多個provider,是以不建議增加connections參數;
  3. 連接配接到達provider時(如dubbo的初次連接配接),首先會判斷總連接配接數是否超限(acceps),超過限制連接配接将被拒絕;
  4. 連接配接成功後,具體的請求交給io thread處理。io threads雖然是處理資料的讀寫,但io部分為異步,更多的消耗的是cpu,是以iothreads預設cpu個數+1是比較合理的設定,不建議調整此參數;
  5. 資料讀取并反序列化以後,交給業務線程池處理,預設情況下線程池為fixed,且排隊隊列為0(queues),這種情況下,最大并發等于業務線程池大小(threads),如果希望有請求的堆積能力,可以調整queues參數。如果希望快速失敗由其他節點處理(官方推薦方式),則不修改queues,隻調整threads;
  6. execute limit(參數executes)是方法級别的并發限制,原理與actives類似,隻是少了等待的過程,即受限後立即失敗;
  7. tps,控制指定時間内(預設60s)的請求數。注意目前dubbo預設沒有支援該參數

從上面的分析,可以看出如果 (consumer數 * actives > provider數 * threads) 且 (queues=0),則會存在部分請求無法申請到資源,重試也有很大幾率失敗。

當需要對一個接口的不同方法進行不同的并發控制時使用executes,否則調整threads就可以。

内容引用自 dubbo參數調優說明

dubbo filter

dubbo提供了多個和請求相關的filter 我們可以看到:ActiveLimitFilter ExecuteLimitFilter TPSLimiterFilter
  • ActiveLimitFilter

    @Activate(group = Constants.CONSUMER, value = Constants.ACTIVES_KEY)

作用于用戶端,控制用戶端同樣的方法可同時運作的次數【即該方法的并發度】
```
dubbo:reference   
配置類:org.apache.dubbo.config.ReferenceConfig
actives:(int default 0) 每服務消費者每服務每方法最大并發調用數  2.0.5以上版本
```
```
dubbo:consumer
配置類: org.apache.dubbo.config.ConsumerConfig   
同時為 dubbo:reference 預設值設定  
default.actives:(int default 0) 每服務消費者每服務每方法最大并發調用數 2.0.5以上版本
```
```
dubbo:provider
配置類: org.apache.dubbo.config.ProviderConfig
同時為 dubbo:service 和 dubbo:protocol 預設值設定
default.actives:(int default 0) 每服務消費者每服務每方法最大并發調用數 2.0.5以上版本
```
           

actives:消費者端的最大并發調用限制,即當 Consumer 對一個服務的并發調用到上限後,新調用會阻塞直到逾時,在方法上配置 dubbo:method 則針對該方法進行并發限制,在接口上配置 dubbo:service,則針對該服務進行并發限制

建議在 Provider 端配置的 Consumer 端屬性

當超過了指定的active值之後該請求将等待前面的請求完成

何時結束呢?依賴于該方法的timeout 如果沒有設定timeout的話 可能就是多個請求一直被阻塞然後等待随機喚醒吧

搭配timeout一起使用

  • ExecuteLimitFilter

    @Activate(group = Constants.PROVIDER, value = Constants.EXECUTES_KEY)

服務端限制
```
dubbo:service  
配置類:org.apache.dubbo.config.ServiceConfig  
executes:(int default 0) 服務提供者每服務每方法最大可并行執行請求數 2.0.5以上版
```
```
dubbo:provider  
配置類: org.apache.dubbo.config.ProviderConfig  
同時為 dubbo:service 和 dubbo:protocol 預設值設定  
default.actives:(int default 0) 服務提供者每服務每方法最大可并行執行請求數 2.0.5以上版
```
           
一旦超出指定的數目直接報錯 其實是指在服務端的并行度【需要注意這些都是指的是在單台服務上而不是整個服務叢集】
  • TpsLimitFilter

    @Activate(group = Constants.PROVIDER, value = Constants.TPS_LIMIT_RATE_KEY)

同樣作用在服務端 目的在于控制一段時間類中的請求數。dubbo沒有預設支援。
```
使用TpsLimitFilter  
Dubbo架構并沒有預設通過配置檔案啟動這個Filter,  
是以我們需要在classpath的META-INF/dubbo/目錄下增加com.alibaba.dubbo.rpc.Filter檔案  
tps=com.alibaba.dubbo.rpc.filter.TpsLimitFilter
就算加上了這個配置,其實也還是生效不了,我們的provider url需要有tps=xxx參數  

相關操作比較繁瑣,dubbo沒有提供預設實作自有其不推薦使用的理由
```
           
Dubbo之限流TpsLimitFilter源碼分析–簡書

dubbo Sentinel

Sentinel 限流、熔斷、降級

Sentinel 開源位址:https://github.com/alibaba/Sentinel

Sentinel以流量為切入點,從流量控制、熔斷降級、系統負載保護等多個次元保護服務的穩定性

Spring Cloud Alibaba

Sentinel 為 Dubbo 服務保駕護航

Sentinel

dubbo Hystrix

Hystrix 主要用于熔斷降級

讨論

  • dubbo 全局限流實作方案及是否有必要?

API限流(http)

Nginx

對于Nginx接入層限流可以使用Nginx自帶了兩個子產品:

連接配接數限流子產品ngx_http_limit_conn_module和漏桶算法實作的請求限流子產品ngx_http_limit_req_module。

  • ngx_http_limit_conn_module

    我們經常會遇到這種情況,伺服器流量異常,負載過大等等。對于大流量惡意的攻擊通路,會帶來帶寬的浪費,伺服器壓力,影響業務,往往考慮對同一個ip的連接配接數,并發數進行限制。ngx_http_limit_conn_module 子產品來實作該需求。該子產品可以根據定義的鍵來限制每個鍵值的連接配接數,如同一個IP來源的連接配接數。并不是所有的連接配接都會被該子產品計數,隻有那些正在被處理的請求(這些請求的頭資訊已被完全讀入)所在的連接配接才會被計數。

    我們可以在nginx_conf的http{}中加上如下配置實作限制:

    #限制每個使用者的并發連接配接數,取名one 
    limit_conn_zone $binary_remote_addr zone=one:10m;
    配置記錄被限流後的日志級别,預設error級别
    limit_conn_log_level error;
    #配置被限流後傳回的狀态碼,預設傳回503
    limit_conn_status 503;	
               
    然後在server{}裡加上如下代碼:
    #限制使用者并發連接配接數為1
    limit_conn one 1;
               

    然後我們是使用ab測試來模拟并發請求:

    ab -n 5 -c 5 http://10.23.22.239/index.html

    得到下面的結果,很明顯并發被限制住了,超過門檻值的都顯示503

    另外剛才是配置針對單個IP的并發限制,還是可以針對域名進行并發限制,配置和用戶端IP類似。

    #http{}段配置
    limit_conn_zone $ server_name zone=perserver:10m;
    #server{}段配置
    limit_conn perserver 1;	
               
  • ngx_http_limit_req_module

    上面我們使用到了ngx_http_limit_conn_module 子產品,來限制連接配接數。那麼請求數的限制該怎麼做呢?這就需要通過ngx_http_limit_req_module 子產品來實作,該子產品可以通過定義的鍵值來限制請求處理的頻率。特别的,可以限制來自單個IP位址的請求處理頻率。 限制的方法是使用了漏鬥算法,每秒固定處理請求數,推遲過多請求。如果請求的頻率超過了限制域配置的值,請求處理會被延遲或被丢棄,是以所有的請求都是以定義的頻率被處理的。

    在http{}中配置

    #區域名稱為one,大小為10m,平均處理的請求頻率不能超過每秒一次。
    limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;
               
    在server{}中配置
    #設定每個IP桶的數量為5
    limit_req zone=one burst=5;
               

    上面設定定義了每個IP的請求處理隻能限制在每秒1個。并且服務端可以為每個IP緩存5個請求,如果操作了5個請求,請求就會被丢棄。

    使用ab測試模拟用戶端連續通路10次:ab -n 10 -c 10 http://10.23.22.239/index.html

    設定了桶的個數為5個。一共10個請求,第一個請求馬上被處理。第2-6個被存放在桶中。由于桶滿了,沒有設定nodelay是以,餘下的4個請求被丢棄。

  • openresty(nginx+lua)

    OpenResty實作限流的幾種方式

spring cloud的zuul

Spring Cloud:服務網關 Zuul
  • Zuul 是開源的微服務網關,可與 Eureka、Ribbon、Hystrix 等元件配合使用,Zuul 它的核心是一系列過濾器,這些過濾器可完成下面功能:
    • 身份認證與安全:識别每個資源的驗證要求,并拒絕那些要求不符合的請求
    • 稽核與監控:在邊緣位置追蹤有意義的資料和統計結果,進而帶來精确的生産視圖
    • 動态路由:動态的将請求路由到不同的後端叢集
    • 壓力測試:逐漸增加指向叢集的流量,以了解性能
    • 負載配置設定:為每一種負載類型配置設定對應容量,并棄用超出限定值的請求
    • 靜态響應處理:在邊緣位置直接建立部分響應,進而避免轉發到内部叢集
    • 多區域彈性:跨越 AWS Region 進行請求路由,實作 ELB 使用多樣化,讓系統邊緣更貼近使用者
  • Spring Cloud 對 Zuul 進行了整合和增強,Zuul 預設使用的 HTTP 用戶端是 Apache Http Client,也可使用 RestClient 或 okHttpClient
内容引用 Spring Cloud:服務網關 Zuul

spring cloud Hystrix

Hystrix 主要用于熔斷降級
  • Hystrix的設計原則
    • 防止單個服務的故障耗盡整個服務的Servlet容器(如Tomcat)的線程池資源。
    • 快速失敗機制,如果某個服務出現了故障,則調用該服務的請求快速失敗,而不是線程等待。
    • 提供回退方案,在請求發生故障時,提供設定好的回退方案。
    • 使用熔斷器機制,防止故障擴散到其他的服務。
    • 提供熔斷器的監控元件Hystrix Dashboard,可以實時監控熔斷器的狀态。
  • Hystrix的工作機制
    • 當服務的某個API接口的失敗次數在一定時間内小于設定的門檻值時,熔斷器處于關閉狀态,該API接口正常提供服務。
    • 當該API接口處理請求的失敗次數大于設定的門檻值時,Hystrix判定該API接口出現了故障,打開熔斷器,這時該API接口會執行快速失敗的邏輯,不執行業務邏輯,請求的線程不會處于阻塞狀态。
    • 處于打開狀态的熔斷器在一定時間後會處于半打開狀态,并将一定數量的請求執行正常邏輯,剩餘的請求會執行快速失敗。
    • 若執行正常邏輯的請求失敗了,則熔斷器繼續打開,若成功了,則熔斷器關閉。這樣熔斷器就具有了自我修複的功能。
内容引用 熔斷器Hystrix