一:前情導讀
高併發環境下若生產者不能及時處理請求造成大量請求線程積壓,最終會演變爲大面積服務崩潰現象產生。根據服務特點設定合理的請求拒絕策略,保證服務正常運行是本文重點。當然必須區別於負載均衡只能分配流量而不能限制流量
二:消費端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屬性存儲當前調用數量