前言
集群中通常一個服務有多個服務提供者。其中部分服務提供者可能由于網(wǎng)絡,配置,長時間 fullgc ,線程池滿,硬件故障等導致長連接還存活但是程序已經(jīng)無法正常響應。單機故障剔除功能會將這部分異常的服務提供者進行降級,使得客戶端的請求更多地指向健康節(jié)點。當異常節(jié)點的表現(xiàn)正常后,單機故障剔除功能會對該節(jié)點進行恢復,使得客戶端請求逐漸將流量分發(fā)到該節(jié)點。單機故障剔除功能解決了服務故障持續(xù)影響業(yè)務的問題,避免了雪崩效應。可以減少人工干預需要的較長的響應時間,提高系統(tǒng)可用率。
這種功能叫做自動故障剔除。
而 SOFA 是怎么實現(xiàn)的呢?
如何使用
自動故障剔除的運行機制:
- 單機故障剔除會統(tǒng)計一個時間窗口內的調用次數(shù)和異常次數(shù),并計算每個服務對應ip的異常率和該服務的平均異常率。
- 當達到ip異常率大于服務平均異常率到一定比例時,會對該服務+ip的維度進行權重降級。
- 如果該服務+ip維度的權重并沒有降為0,那么當該服務+ip維度的調用情況正常時,則會對其進行權重恢復。
- 整個計算和調控過程異步進行,不會阻塞調用。
根據(jù)官方例子,使用方式如下:
FaultToleranceConfig faultToleranceConfig = new FaultToleranceConfig();
// 是否開啟調控.
faultToleranceConfig.setRegulationEffective(true);
// 是否進行降級
faultToleranceConfig.setDegradeEffective(true);
// 時間窗口大小
faultToleranceConfig.setTimeWindow(20);
// 每次權重降級的比率
faultToleranceConfig.setWeightDegradeRate(0.5);
FaultToleranceConfigManager.putAppConfig("appName", faultToleranceConfig);
如上,該應用會在打開了單機故障剔除開關,每20s的時間窗口進行一次異常情況的計算,如果某個服務+ip的調用維度被判定為故障節(jié)點,則會進行將該服務+ip的權重降級為0.5倍。
可以看到,這個功能面向框架用戶的 API 就是這個 FaultToleranceConfig 類,即故障容錯配置。
用戶可以配置某個服務是否開啟調控,是否進行降級,實際窗口大小(秒),每次權重降級的比率。
那么,SOFA 是如何實現(xiàn)的呢?
源碼分析
首先說明,由于這個功能相比較之前的功能,代碼要復雜一些,因此,本次分析主要分析主流程,不會面面俱到。關于詳細的代碼細節(jié),我們將在后面的源碼分析中詳細解釋。
1. 初始化
SOFA 對于該功能的設計使用了 Modle 的方式,簡單來說,就是一個可擴展,可熱插拔的中間件。SOFA 的中間件和 RPC 框架都是通過 Modle 的方式來實現(xiàn)的。
如何實現(xiàn)呢?
RPC 初始化的時候,會調用 RpcRuntimeContext 類的靜態(tài)塊,該靜態(tài)塊內部會初始化其他模塊,即調用 ModuleFactory.installModules() 方法。該方法會加載配置文件中所有 Module 接口的 SPI 文件。然后循環(huán)調用 install 方法,即初始化。
目前的源碼中只有一個 Module 的實現(xiàn)類,即 FaultToleranceModule 。故障容錯模塊。該類的 install 方法 會創(chuàng)建一個訂閱者,在事件總線中訂閱兩個事件:ClientSyncReceiveEvent 和 ClientAsyncReceiveEvent。然后創(chuàng)建一個事件窗口調控器。并初始化該調控器。
TimeWindowRegulator 是故障調控的核心類,內部包含以下屬性:
- measureCounter 度量計數(shù)器,每執(zhí)行一次度量任務,就加一。
- measureScheduler 度量定時任務線程池,使用 RATE 模式,即從任務開始時間開始計算。如果任務的時間超過了間隔時間,間隔時間將失效。這里的間隔時間是 1 秒。
- regulationExecutor 計算線程池,即在定時任務線程池中提交計算任務給這個線程池,以實現(xiàn)快速返回。該線程池核心大小為 2.
- measureModels 度量模型,一個存放 MeasureModel 對象的 List。
- measureStrategy 計算策略(根據(jù)度量結果,判斷是否需要執(zhí)行降級或者恢復)
- weightDegradeStrategy 降級策略: 調整權重
- logDegradeStrategy 降級策略: 只打印日志
- recoverStrategy 恢復策略:調整權重
- listener 調用統(tǒng)計監(jiān)聽器,當發(fā)生事件時,會調用監(jiān)聽器的方法。
屬性很多,暫時不詳細解釋。說主流程。
該類的 intit 方法是初始化這些屬性,并注冊監(jiān)聽器。注冊方式是調用 InvocationStatFactory.addListener(listener) 方法。而這個監(jiān)聽器是該類的內部類 —— TimeWindowRegulatorListener。
好,初始化完畢之后,開始說流程。
當 RPC 框架調用了發(fā)送消息的方法,并返回(或者失敗)后,就會向事件總線 EventBus 丟一個事件。例如 ClientSyncReceiveEvent 事件,該事件需要包含以下屬性:
private final ConsumerConfig consumerConfig;
private final ProviderInfo providerInfo;
private final SofaRequest request;
private final SofaResponse response;
private final Throwable throwable;
基本包含了此次調用的所有信息。
此時,就會觸發(fā)訂閱者的 onEvent 方法。即 FaultToleranceSubscriber 的 onEvent 方法。該方法會判斷,如果用戶啟用了自動故障剔除功能,則根據(jù) consumerConfig 和 providerInfo 得到一個調用統(tǒng)計器對象,并對調用次數(shù)加一。
關鍵的方法在 onEvent 中調用的 InvocationStatFactory.getInvocationStat(consumerConfig, providerInfo); 該方法會創(chuàng)建一個 InvocationStat 調用統(tǒng)計器對象,放入 Map 中,而對應的 key 則是根據(jù)上面兩個參數(shù)生成的 InvocationStatDimension 統(tǒng)計維度對象。
創(chuàng)建完 InvocationStat 調用統(tǒng)計器對象后,調用所有監(jiān)聽器的 onAddInvocationStat 方法,表示,我添加了一個監(jiān)聽器了,你可以做點什么了。還記得 TimeWindowRegulator 初始化的時候會添加一個監(jiān)聽器嗎。就是這里的監(jiān)聽器。
內部類 TimeWindowRegulatorListener 的方法邏輯如下:
調用度量策略對象的 buildMeasureModel 方法,傳入調用統(tǒng)計器。返回一個度量模型。然后,將這個模型添加進 List(measureModels 屬性) 中。并調用外部類的 startRegulate 方法,開始進行調控。
startRegulate 方法就是啟動了定時任務,使用了一個原子 boolean 變量進行狀態(tài)判斷。
定時任務的內容是什么呢?
答:運行 MeasureRunnable 任務。
該任務首先會對度量計數(shù)器加一。然后循環(huán) List 中的 MeasureModel 度量模型。并判斷該 MeasureModel 是否到達了用戶設定的時間窗口(取于用戶設置的時間大小)。
如果到達了時間窗口,并調用 measureStrategy 度量策略對象(serviceHorizontal)的 measure 方法,參數(shù)則是度量模型,返回一個度量的結果對象 —— MeasureResult。
得到這個對象后,向計算線程池提交一個 RegulationRunnable 任務,該任務內容如下:
拿到剛剛傳入的度量結果拿到度量結果詳情的集合 —— measureResultDetails,循環(huán)這些集合,并執(zhí)行 doRegulate 方法,進行調控。
該方法就是真正的對服務進行調控的方法了。首先,一個服務有 3 個狀態(tài):健康,異常,忽略。狀態(tài)來自于剛剛的 measure 方法。
如果用戶設置了可以降級的話,則判斷服務的度量狀態(tài),如果異常了且不超過一個服務的最大調控 IP 數(shù),則執(zhí)行權重降級邏輯。反之,打印日志。
如果度量狀態(tài)是正常的,則執(zhí)行權重恢復,并從降級 IP 列表中刪除。
如果用戶沒有設置可以降級,且度量狀態(tài)是異常,那么,執(zhí)行日志降級。即對異常 IP 記性異常信息的日志打印。
當對權重進行降級之后,能夠被負載均衡擊中的幾率就會對應的小很多。甚至了無法擊中。
以上,就是 SOFA 自動故障剔除功能的基本實現(xiàn)流程。
總結
還是那句話,由于這個功能比較繁雜,限于篇幅,今天看的是總流程,總結一下這個流程。
RPC 框架在啟動的時候,會自動加載故障容錯模塊,并監(jiān)聽客戶端發(fā)送事件。同時會初始化故障容錯相關的類和監(jiān)聽器。
當發(fā)生訂閱事件的時候,會調用 onEvent 方法,進而調用 TimeWindowRegulatorListener 的監(jiān)聽器方法。該方法會將度量模型添加進 List 中。
定時任務每隔一秒會調度 MeasureRunnable 任務,內容是根據(jù)用戶設置的時間窗口處理 List 中的調度模型。
定時任務會向計算任務線程池提交一個 RegulationRunnable 任務。用于處理度量結果中的數(shù)據(jù)。該任務會循環(huán)度量結果的所有度量結果詳情,并調用 doRegulate 方法進行調控。
最后,doRegulate 方法則是根據(jù) 度量結果詳情 的狀態(tài)判斷是否應該對服務 + IP 進行權重降級或者權重恢復,再或者是打印日志 —— 這依據(jù)用戶設置。
以上就是 SOFA 自動故障剔除的基本流程。后面我們會詳細分析自動故障剔除的細節(jié)代碼。敬請期待。