系統穩定性建設 -- 依賴關係治理

系統高可用的根基

隨着公司業務不斷的發展,系統也在變得越來越複雜。系統的複雜度體現在:前端對後端的依賴,後端服務之間的依賴。在沒有明確強弱依賴的前提下,我們很難進行熔斷、降級、限流的相關操作,也不能有效的對系統進行相關優化改造、持續推進系統穩定性提升。

一、強弱依賴的定義

在聊強弱依賴之前,我們先看下公司的服務分級標準:

S1 核心  S2 次核心 S3 非核心 S4 其他

S1 級別:影響業務線核心業務流程,會影響用戶使用

S2 級別:不涉及核心主業務流程,服務不可用會造成大範圍用戶體驗下降。

S3 級別:不涉及核心業務流程,應用不可用對用戶幾乎無影響,如頭像、修改資料等一些長尾邊緣應用。

S4 級別:應用不可用對線上幾乎無影響,如運營後臺,如後臺運營策略

在這裏,我們對強弱依賴的定義即可得出:異常發生時,不影響核心業務流程,不影響系統可用性的依賴稱作弱依賴,反之爲強依賴。

二、強弱依賴治理

強弱依賴治理就是通過科學的手段持續穩定地得到應用間依賴關係、流量、強弱等數據,提前發現因爲依賴問題可能導致的故障,避免依賴故障影響用戶體驗,積累數據持續推進系統穩定性提升。

三、兩輪強弱依賴治理最佳實踐

1、發現

1.1、人工梳理

在前期,我們通過投入相當人力,通過代碼走讀的形式將用車核心鏈路上的所有依賴進行梳理。

如何鑑定強弱依賴

先確定主業務,再評定依賴的服務對主業務有沒有影響

eg:對於用戶掃碼業務,會有很多前置校驗,比如用戶用車資格、車輛信息和狀態檢查等。

以的早期用車系統某服務爲例(以下簡稱 ride):

如上圖所示,通過人工梳理發現,核心業務鏈路只有:創建訂單 / 開始訂單 / 結束訂單 / 查詢訂單等,但服務依賴卻有幾十個,redis 實例、數據庫、mq 等資源依賴也很多,究其原因,是因爲 ride 服務一共提供了幾十個接口,除了前面說到的 4、5 個接口是核心接口外,其他均爲非核心接口,還有很多後置處理的業務邏輯,導致 ride 服務依賴很多核心流程不相關的弱依賴服務和資源,大大影響了服務的穩定性。

1.2、故障演練

後期,尤其故障演練在兩輪的逐漸落地

我們通過服務配置文件整理服務依賴,然後通過故障演練,在線下逐個對依賴注入異常,驗證主業務是否可用。從而鑑別強弱依賴。

目前我們正在運用故障演練,通過給兩輪非 S1 核心服務注入異常,來識別核心鏈路的強弱依賴,防止核心鏈路上前、後端的劣化。這個點會在後續的文章《穩定性建設系列文章 -- 兩輪故障演練最佳實踐》詳細展開,敬請期待。

2、改造 & 預案

2.1  前、後端容錯改造

針對對核心業務有影響的場景和 case,推動前後端去做容錯改造。

2.1.1 前端:將非核心流程後端接口依賴解耦,異常情況下不阻塞核心流程

以確認開鎖頁爲例,在確認開鎖頁,會去拉取各種區域需收取的調度費規則,以及騎行費用計價規則,最後會加載 “確認開鎖” 按鈕

            

如果此頁面對拉取這些計費規則的接口是強依賴,例如當後端聚合這些信息的接口出現故障時,將導致 “確認開鎖” 按鈕加載失敗,業務流程無法推進,嚴重影響用戶用車。

如果此頁面對拉取這些計費規則的接口是弱依賴,例如當後端聚合這些信息的接口出現故障時,會導致部分計價規則信息內容加載不全,但核心業務流程仍可繼續推進,用戶體驗並未受到太多影響。

故,前端需要將此類後端調用做好容錯,進行解耦,將其變成弱依賴,使核心業務流程仍可繼續推進。

2.1.2  後端:對弱依賴的異常做合理的捕獲邏輯,配置合理的超時、熔斷以及限流

設置合理的超時

從用戶體驗和系統穩定性角度出發,有關網絡調用的請求,都需要配置超時。在服務端設置超時時,需要考慮到業務本身的執行耗時,加上序列化和網絡通訊的時間。一般可按照接口 RT 的 95 或 99 線來設置超時間。當然客戶端也可以根據自己的業務場景配置超時時間,例如一些前端應用,需要用戶快速看到結果,可以把超時時間設置小一些。

設置熔斷

某個服務故障或者異常時,如果該服務觸發熔斷,可以防止其他調用方一直等待超時或者故障,從而防止雪崩,只要是弱依賴服務,並且能設計合理的獲取返回值的方案(返回值可以是默認值,或者通過一種後備(Fallback)方案獲取的值),一般業務場景都可以做熔斷處理。比如服務異常時我們可以熔斷後走默認邏輯,讓通過校驗,既使核心業務流程仍可繼續推進,也使得服務訪問變低,給予了系統恢復的窗口。

設置限流

底層核心服務接口,以及核心對外的接口一定要做好限流,保證業務系統不會被大量突發請求擊垮,提高系統穩定性。

2.2  核心與非核心業務隔離

線程級隔離

通過信號量或者線程池等技術實現線程隔離

進程級隔離

1、業務拆分

將非核心業務與核心業務進行服務拆分

2、分組部署

進行分組部署,讓上游調核心和非核心調用訪問不同的分組

下面以 ride 服務的治理爲例,來說明在當時上線時間緊迫、穩定性要求高的背景下核心與非核心業務隔離這三種方式應如何做取捨。

首先,由於 rpc 請求調用的線程是由 soa 框架創建的,所以通過信號量或者線程池等技術實現線程隔離不可行;然後對於分組部署,因爲當時的 ride 服務有多領域的業務邏輯,雖然分組部署能夠解決運行時業務流量隔離的問題,但是沒法避免因代碼變更、發佈引起的系統不穩定性。所以,我們最後選擇了進行業務拆分,並且因爲我們已經能夠明確核心業務的範圍以及核心業務上下游的依賴,影響範圍可控,故最終我們決定將核心業務從服務中拆出,而不是將非核心業務拆出。如下圖:

2.3   人工降級開關
針對具體的業務場景,以場景爲最小單位,編寫資損可控的業務兜底,配置相應的動態切換開關,異常發生時,可一鍵切換至兜底邏輯。

3、驗證

我們通過服務配置文件服務依賴,然後通過故障演練,在線下逐個對依賴注入異常,驗證主業務是否可用。從而鑑別和驗證強弱依賴。

四、現狀 & 問題

比較多關注服務與服務之間的依賴關係,忽略了對中間件 (redis/hbase/mq 等) 的依賴

比較多關注運行時階段,忽略了對於啓動階段和停止階段的治理。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/VwSuYx7SXnIOxDQ2Lm84hg