Dubbo調優(二) -- 限流策略
一:前情導讀
高併發環境下若生產者不能及時處理請求造成大量請求執行緒積壓,最終會演變為大面積服務崩潰現象產生。根據服務特點設定合理的請求拒絕策略,保證服務正常執行是本文重點。當然必須區別於負載均衡只能分配流量而不能限制流量
二:消費端actives
僅針對消費者端生效,只能在<dubbo:reference>
亦或是其子標籤<dubbo:method>
或者是<dubbo:consumer>
中配置。優先順序策略與文章Dubbo調優 -- 超時TimeOut描述一致
2.1 配置示例
- dubbo:consumer中配置針對所有服務所有方法生效
-
dubbo:consumer
- dubbo:method中配置針對該方法生效
2.2 引數詳解
描述 | 備註 |
---|---|
作用 | 消費者最大併發數量限制,超過限制將會丟擲異常 |
實現 | 過濾器Filter,具體實現子類為ActiveLimitFilter |
預設值 | 0表示沒有限制 |
配置地點 | <dubbo:consumer>、<dubbo:reference> 、<dubbo:method> |
2.3 原始碼導讀
- 處理請求引數:URL為Dubbo封裝的一個請求物件類,包含Map<String,String>型別屬性numbers,該屬性中含有actives配置
- 請求過濾判斷:RpcStatus類封裝生產者呼叫狀態,AtomicInteger原子型別active屬性儲存當前呼叫數量。通過其與URL中獲取到的對應引數屬性值比較判斷
- 請求返回結果:如果允許則進行下一步RPC呼叫,不允許則會暫停等待執行緒timeout引數時長,若喚醒還未有空餘執行緒則丟擲異常
三:消費端connections
大家熟悉的HTTP協議就屬於短連線,每次請求的時候都會多次驗證握手建立連線。預設的Dubbo協議屬於長連線,採用NIO非同步傳輸,每消費者與生產者之間預設採用單一長連線方式通訊。換個簡單說法就是每個消費者與生產者之間長連線預設就建立一個,所有請求共用
connections引數針對上述長連線與短連線具備不同作用效果:
- 短連線因為是多連線所以限制其個數
- 長連線因為是單一連線所以是指定其建立數量
3.1 配置示例
connections引數生效的位置在消費端,圖一表示消費端的配置,圖二表示在生產者的配置。根據自身測試以及github驗證,生產端的配置確實會通過註冊中心傳遞給消費端生效
3.2 引數詳解
描述 | 備註 |
---|---|
作用 | 限制消費者短連線數量,長連線建立數量 |
實現 | 初始化連線時根據引數控制 |
預設值 | 長連線預設表示使用JVM共享長連線,線上一般都是多生產多消費,這個引數不建議更改 |
配置地點 | <dubbo:consumer>、<dubbo:reference> 、<dubbo:provider>、<dubbo:service> |
3.3 原始碼導讀
首先專案初始化的時候會根據connections引數初始化連線,過程在DubboProtocol類的getClients()方法中,下圖是debug跟進的初始化結果。可以看到用於儲存連線的陣列最後返回的是兩個連線例項
連線使用發生在類DubboInvoker中,該類的方法doInvoke()用於執行呼叫邏輯。使用的連線就是在DubboProtocol類中getClients()初始化出來並在方法refer()中放入DubboInvoker物件的連線。如下圖所示是DubboInvoker中doIncoke()使用連線的關鍵程式碼四:生產端accepts
消費者可以通過connections引數設定連線的數量,但是如果生產者不進行自我保護,採用預設的無限制連線策略。高併發情況下生產者可能就會因為連線數量巨大崩潰,這時可以通過引數accepts限制生產者可接受最大連線數量
4.1 配置示例
accepts用於生產者限制最大連線數量保護自身服務可用性,可以在標籤<dubbo:protocol>
中進行配置。這時候在<dubbo:reference>
中設定connections超過accepts值,用於方便後續的原始碼跟進
4.2 引數詳解
描述 | 備註 |
---|---|
作用 | 限制生產者最大可接受連線數量,用於保護生產者自身 |
實現 | 消費者初始化建立連線時會開啟建立連結,這時候就會根據限制引數判斷 |
預設值 | 0表示沒有限制,比較危險的配置 |
配置地點 | <dubbo:protocol> |
4.3 原始碼導讀
生產者啟動初始化過程中可以看到開啟連線的時候獲取了引數accepts的設定,過程在AbstractServer類建構函式中可以看到
消費端初始化的時候當超過生產者限制連線數量後,在AbstractClient類中可以看到,建構函式中呼叫方法connect()建立連線。這時候會丟擲異常,因為異常原因是等待建立連線超時3000ms。驗證引數accepts效果五:生產端執行緒池
多執行緒併發操作一定離不開執行緒池,Dubbo自身提供了支援了四種執行緒池型別支援。生產者<dubbo:protocol>
標籤中可配置執行緒池關鍵引數,執行緒池型別、阻塞佇列大小、核心執行緒數量等
5.1 iothreads、threads
-
iothreads:限制的是io執行緒池大小,該執行緒池執行緒用於處理Dubbo框架自身業務邏輯。預設值為
CPU+1
,不建議更改設定 -
threads:用於指定下面講到的業務執行緒池執行緒數量,這個才是業務需要關心的執行緒數量。預設大小
200
5.2 threadpool
引數threadpool指定使用執行緒池型別,Dubbo中自身實現提供瞭如下表所示四種執行緒池。預設使用固定大小執行緒池FixedThreadPool
型別名稱 | 佇列型別 | 特性備註 |
---|---|---|
FixedThreadPool |
queues 屬性為0建立無容量阻塞佇列SynchronousQueue ,若 queues 小於0則建立Integer.MAX_VALUE容量LinkedBlockingQueue 阻塞佇列,大於0則建立 queues 引數限定容量LinkedBlockingQueue 阻塞佇列 |
核心執行緒數量與最大執行緒數量一致採用引數threads 值、執行緒空閒存活時間0 |
CachedThreadPool | 佇列建立型別規則與FixedThreadPool 一致 |
相對於固定容量大小FixedThreadPool執行緒池多了引數corethreads 設定核心執行緒數量支援預設0,執行緒空閒存活時間暫時未提供引數設定,預設1分鐘 |
LimitedThreadPool | 佇列建立型別規則與FixedThreadPool 一致 |
相對於CachedThradPool而言最大的變化在於執行緒存活時間修改為Long.MAX_VALUE |
EagerThreadPool | 佇列為Dubbo設計實現的TaskQueue 佇列,該佇列繼承自LinkedBlockingQueue 。當queues 引數小於等於0則其容量為1,若大於0則容量為queues 引數值 |
後面會有專門文章研究這個執行緒池實現 |
5.3 注意
Dubbo官網檔案只描述了fixed/cached,四種執行緒池預設支援的是fixed
六:生產端executes
一個只能在生產者即dubbo:service亦或是其子標籤dubbo:method中配置的屬性,消費者中配置不會生效。這個引數主要目的是在生產者端限制應用執行緒使用數量
6.1 配置示例
限制該服務每個方法併發不超過10,其中dubboProtocolGetMethod方法併發不超過2。方法級別的配置優先順序高於服務配置
6.2 引數詳解
配置地點 | 生產者dubbo:service標籤或其子標籤dubbo:method中 |
---|---|
預設值 | 0表示沒有限制 |
作用 | 服務提供者每個方法只能佔用執行緒池中配置數量執行緒,超出則丟擲異常 |
實現 | 過濾器Filter |
6.3 原始碼導讀
- 主要涉及類:ExecuteLimitFilter,關注相關類RpcStatus、URL
- 主要方法:getMethodParameter()、beginCount()、getStatus()
- 處理請求引數,URL為Dubbo封裝的一個請求物件類,包含Map<String,String>型別屬性numbers,該屬性中含有executes配置
- 提取executes引數值,numbers -- paramters -- 預設值順序返回
- 比較executes值數量,RpcStatus類封裝生產者呼叫狀態,AtomicInteger原子型別active屬性儲存當前呼叫數量