限流
- 根據排隊理論,具有延遲的服務随着請求量的不斷提升,其平均響應時間也會迅速提升,為了保證服務的SLA(Service-Level Agreement 服務等級協定),有必要控制機關時間的請求量。這就是限流為什麼愈發重要的原因。
分類
- qps限流
限制每秒處理請求數不超過門檻值
- 并發限流
限制同時處理的請求數目。Java 中的 Semaphore(信号量) 是做并發限制的好工具,特别适用于資源有效的場景。
- 單機限流
Guava 中的 RateLimiter(内部采用令牌捅算法實作)。
- 叢集限流
redis限流,計時器和計數器處理、key加秒為key
nginx(+lua)前端限流,按照一定的規則如帳号、IP、系統調用邏輯等在Nginx層面做限流
算法
常見的限流算法有:令牌桶、漏桶。計數器也可以進行粗暴限流實作。
- 漏桶算法
- 令牌桶算法
實施方案
RPC限流(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 | 線程池隊列大小,當線程池滿時,排隊等待執行的隊列大小,建議不要設定,當線程程池時應立即失敗,重試其它服務提供機器,而不是排隊,除非有特殊需求。 |
分析
- 當consumer發起一個請求時,首先經過active limit(參數actives)進行方法級别的限制,其實作方式為CHM中存放計數器(AtomicInteger),請求時加1,請求完成(包括常)減1,如果超過actives則等待有其他請求完成後重試或者逾時後失敗;
- 從多個連接配接(connections)中選擇一個連接配接發送資料,對于預設的netty實作來說,由于可以複用連接配接,預設一個連接配接就可以。不過如果你在壓測,且隻有一個consumer,一個provider,此時适當的加大connections确實能夠增強網絡傳輸能力。但線上業務由于有多個consumer多個provider,是以不建議增加connections參數;
- 連接配接到達provider時(如dubbo的初次連接配接),首先會判斷總連接配接數是否超限(acceps),超過限制連接配接将被拒絕;
- 連接配接成功後,具體的請求交給io thread處理。io threads雖然是處理資料的讀寫,但io部分為異步,更多的消耗的是cpu,是以iothreads預設cpu個數+1是比較合理的設定,不建議調整此參數;
- 資料讀取并反序列化以後,交給業務線程池處理,預設情況下線程池為fixed,且排隊隊列為0(queues),這種情況下,最大并發等于業務線程池大小(threads),如果希望有請求的堆積能力,可以調整queues參數。如果希望快速失敗由其他節點處理(官方推薦方式),則不修改queues,隻調整threads;
- execute limit(參數executes)是方法級别的并發限制,原理與actives類似,隻是少了等待的過程,即受限後立即失敗;
- 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{}中加上如下配置實作限制:
然後在server{}裡加上如下代碼:#限制每個使用者的并發連接配接數,取名one limit_conn_zone $binary_remote_addr zone=one:10m; 配置記錄被限流後的日志級别,預設error級别 limit_conn_log_level error; #配置被限流後傳回的狀态碼,預設傳回503 limit_conn_status 503;
#限制使用者并發連接配接數為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{}中配置
在server{}中配置#區域名稱為one,大小為10m,平均處理的請求頻率不能超過每秒一次。 limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;
#設定每個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