Categories
程式開發

微服務治理實踐:探尋業務的單點異常自愈能力


不斷的學習新東西,不斷的思考更多,不斷的對原有自己造成更大的衝擊。如果要給我遷移 FaaS 期間的感受下一個總結,那麼一定是:“在撕裂中成長”。

微服務架構下,穩定性和高可用性一個永恆的話題,在實際的治理過程中,我們有可能會遇到以下場景:

  • 某個應用灰度發布,先上了幾台機器,由於代碼邏輯寫的有問題,造成線程池滿,出現運行異常。
  • 服務端集群中,某幾台機器由於磁盤滿,或者是宿主機資源爭搶導致 load 過高,客戶端出現調用超時。
  • 服務端集群中,某幾台機器由於線程池滿,造成 Full Garbage Collection。

在以上3 種場景中,由於客戶端並不法感知已經出現問題的那些服務端,依然會發送請求到這些機器上,造成業務調用報錯,上游的機子將會被下游的某台機子的短暫故障拖垮,造成應用雪崩的風險。

面對這種場景,如果僅僅為此而進行服務降級,對應用的傷害未免過大,但如果我們可以檢測出服務集群中某些故障機子,並對其進行短暫隔離,即可有效保障服務的高可用與系統的穩定性,同時給運維人員提供了寶貴的緩衝時間,用於問題定位,排除故障。

本文將作為《微服務治理實踐》系列篇的第一篇,為大家介紹如何實現離群實例摘除。該系列文章是基於阿里雲商業化產品 EDAS 的微服務實踐,如果您團隊具備較強的微服務治理能力,那麼希望我們在微服務治理方面的實踐和背後的思考,可以為您提供一些參考。

Microservice Outlier Ejection (微服務離群實例摘除)

  • 什麼是離群實例摘除

當單點發生夯機異常時,consumer 能主動判斷,並將對應的 provider 實例短時間剔除,不再請求,在一定時間間隔後再繼續訪問。同時,具有全局異常判斷能力,當 provider 異常實例的數量過多時,並且超過一定的控制比例,說明此時 provide 整體服務質量低下,該機制僅保持摘除一定的比例。

  • 離群實例摘除的功能

從服務層容錯能力上,對業務穩定性進行增強,有效解決單點故障的問題。

  • 與熔斷的區別

熔斷是指當服務的輸入負載激增時, 避免服務被迅速壓垮導致雪崩效應,而對負載進行斷路的一種方式 。熔斷一般由熔斷請求判斷算法,熔斷恢復機制,熔斷報警等模塊組成。隔離是指,為了避免在依賴的服務故障時候造成的故障擴散,而採取的將系統進行單元化設計的一種架構方法。

若僅僅由於服務端集群中單點異常問題,就採用熔斷降級方案,將會對應用的傷害過大,離群實例摘除可以有效地解決單點異常問題從而保證服務質量。若 provider 整體服務質量低下時,離群摘除效果不再明顯,此時可以採用熔斷降級功能。

  • 離群實例摘除支持的版本

只要您的應用版本在列表中,您無需改動一行代碼就可以使用到離群實例摘除功能。

微服務治理實踐:探尋業務的單點異常自愈能力 1

目前已經覆蓋了市面上大部分微服務場景,後續我們將會持續支持開源最新的 Dubbo/Spring Cloud 版本。

我們提供了 Dubbo 和 Spring Cloud 兩種場景的離群摘除功能,本文將先介紹一下 Dubbo Microservice Outlier Ejection 的實踐與效果。

示例

下面將通過在 EDAS 上通過演示 Dubbo 離群摘除功能及效果。

企業級分佈式應用服務EDAS(Enterprise Distributed Application Service)是一個應用託管和微服務管理的PaaS 平台,提供應用開發、部署、監控、運維等全棧式解決方案,同時支持Dubbo、Spring Cloud 等微服務運行環境。

https://www.aliyun.com/product/edas

準備

接下來以微服務 Demo 為例子示範離群摘除功能,讀者可以從 github 中下載驗證

https://github.com/aliyun/alibabacloud-microservice-demo/tree/master/src

微服務Demo 是一個簡單的電商項目,下圖為項目結構,cartservice 為Dubbo 框架的購物車服務provider,productservice 為Spring Cloud 提供的商品詳情服務provider,frontend 為web controller 即前端展示頁面,可以理解為consumer 。

微服務治理實踐:探尋業務的單點異常自愈能力 2

我們將以 cartservice 服務即 Dubbo 服務端為例子,展示離群實例摘除功能。

EDAS 上部署微服務 Demo

首先cd cartservice 切換到cartservice 目錄下,再通過mvn clean install 打包,通過cd cartservice-provider/target 切換到target 目錄下,我們可以看到新生成的cartservice-provider-1.0.0-SNAPSHOT.jar 包,然後在EDAS上創建一個cartservice 應用。

微服務治理實踐:探尋業務的單點異常自愈能力 3

點擊下一步後,上傳剛才打包的jar包,即 cartservice-provider/target/ cartservice-provider-1.0.0-SNAPSHOT.jar 然後下一步,記住登陸密碼,直到創建應用成功。

微服務治理實踐:探尋業務的單點異常自愈能力 4

然後啟動應用,到目前為止,我們啟動了一個 cartservice-provider。點擊按此實例規格擴容,該服務我們部署在兩個實例上。

微服務治理實踐:探尋業務的單點異常自愈能力 5

我們在這個 provider 的com.alibabacloud.hipstershop.provider.CartServiceImpl 類中可以看到,這個 provider 是提供了viewCart 和 addItemToCart 的兩個關於購物車的服務,我們在 viewCart 中加入一些模擬運行時異常的邏輯。

    @Value("${exception.ip}")
    private String exceptionIp;
    @Override
    public List viewCart(String userID) {
        if (exceptionIp != null && exceptionIp.equals(getLocalIp())) {
            throw new RuntimeException("运行时异常");
        }
        return cartStore.getOrDefault(userID, Collections.emptyList());
    }

其中 exceptionIp 為 ACM 配置中心的 exception.ip 的配置項,若該項配置為本機 Ip 時,該服務 throw RuntimeException ,用於模擬業務異常的場景。

  • 為什麼將 cartservice 擴容到兩個實例,想必大家也猜到了,運行時通過配置ACM配置中心指定其中一個實例的IP,模擬出一個實例異常的場景。

接下來,我們需要部署frontend / productservice 兩個服務,方式一樣,分別上傳frontend/target/frontend-1.0.0-SNAPSHOT.jar 和productservice/productservice-provider/target/productservice-provider-1.0.0-SNAPSHOT. jar。

從下圖可以看到,我們的微服務 Demo 在 EDAS 部署上去了。

微服務治理實踐:探尋業務的單點異常自愈能力 6

模擬業務異常

進入到 frontend 應用中,我們看到其實例的公網 Ip 為 47.99.150.33。

微服務治理實踐:探尋業務的單點異常自愈能力 7

進入瀏覽器訪問 :

http://47.99.150.33:8080/

微服務治理實踐:探尋業務的單點異常自愈能力 8

點擊 View Cart 訪問至 :

http://47.99.150.33:8080/cart

微服務治理實踐:探尋業務的單點異常自愈能力 9

可以看到,此時服務都是正常的。

我們去 ACM 配置中心 配置 exception.ip 為 172.16.205.180(即cartservice的其中某個實例的IP)。

微服務治理實踐:探尋業務的單點異常自愈能力 10

然後繼續訪問 :

http://47.99.150.33:8080/cart

發現 50 % 的概率錯誤頁面。

微服務治理實踐:探尋業務的單點異常自愈能力 11

此時,我們寫一個腳本,定時大量訪問 :

http://47.99.150.33:8080/cart

模擬請求。


while :
do
        result=`curl $1 -s`
        if (( "$result" == *"500"* )); then
                echo `date +%F-%T` $result
        else
                echo `date +%F-%T` "success"
        fi
        sleep 0.1
done

然後 sh curlservice.sh http://47.99.150.33:8080/cart。

我們看到不斷重複的每秒鐘 10 次的 50% 的調用成功率。

微服務治理實踐:探尋業務的單點異常自愈能力 12

其實也可以理解到,下游的服務質量隨著上游的某台機子的異常而急劇下降,甚至可能導致下游服務被上游某些機子的(系統、業務)異常給拖垮。

開啟離群摘除策略

下面我將演示離群摘除的策略的開啟及其效果的展示。

創建

我們進入到 EDAS 左側列表的 (微服務管理) 下的 (離群實例摘除) 界面中,並選擇創建離群實例摘除策略。

微服務治理實踐:探尋業務的單點異常自愈能力 13

然後按照提示一步步創建離群摘除的策略。

基本信息

微服務治理實踐:探尋業務的單點異常自愈能力 14

如上圖可以選擇命名空間、填寫策略名稱、選擇該策略支持的框架類型(Dubbo/Spring Cloud)。

選擇生效應用

微服務治理實踐:探尋業務的單點異常自愈能力 15

按照目前的調用方式,我們只需要配置 frontend 應用,保護下游應用 consumer。

配置策略

微服務治理實踐:探尋業務的單點異常自愈能力 16

這些參數都提供了默認值,需要根據自己應用的具體情況調整最合適的值,由於需要保護的 RuntimeException 屬於業務異常於是選上 網絡異常+業務異常。 (需要注意的是即使摘除實例比例上限配得特別低,向下取整數小於1,當集群中實例數目大於1,且某一實例異常,我們也會摘除一實例)。

創建完成

微服務治理實踐:探尋業務的單點異常自愈能力 17

可以看到策略的信息,創建完成。

策略

微服務治理實踐:探尋業務的單點異常自愈能力 18

看到了我們創建的離群摘除策略,且是針對 Dubbo 框架,並且針對的是 網絡異常+業務異常 的異常類型。

驗證離群摘除效果

這時,我們看到,再感知到異常後,離群摘除功能生效,請求調用一陣子後,均返回正確結果。

微服務治理實踐:探尋業務的單點異常自愈能力 19

不斷刷新瀏覽器訪問:

http://47.99.150.33:8080/cart

也均正常。

微服務治理實踐:探尋業務的單點異常自愈能力 20

客戶端感知到某台服務端機子異常後,主動摘除。僅僅調用業務正常的 Provider 實例,同時我們也可以通 ARMS(EDAS監控系統) 監控看到服務質量的上升,以及流量從異常 Provider 中摘除。

Dubbo 框架可以從 /home/admin/.opt/ArmsAgent/logs 目錄下的日誌中,搜索日誌中的 “OutlierRouter” 關鍵字可以看到一系列離群實例摘除的事件日誌。

修改/關閉離群摘除策略

對於 EDAS 的應用我們支持通過控制台動態修改和刪除離群摘除策略。

  • 對應策略規則的修改

點擊 修改生效應用 或者 編輯策略。

微服務治理實踐:探尋業務的單點異常自愈能力 21

然後增加刪除應用或者調整參數,確定後均立即生效

  • 刪除對應策略

微服務治理實踐:探尋業務的單點異常自愈能力 22

控制台的操作,對應用中的配置都是實時生效的,若刪除策略後,默認關閉相關策略。

若我們打開 ARMS 監控觀察具體的調用情況。

ARMS監控

若我們開啟監控,將會直觀看到流量與請求錯誤等信息。

開啟離群摘除前

如下圖方式開啟,然後跳轉至 ARMS( EDAS 監控系統)應用監控頁面,我們需要把三個應用都開啟高級監控。

微服務治理實踐:探尋業務的單點異常自愈能力 23

我們可以從下圖即 ARMS( EDAS 監控系統)應用監控頁面直觀地看到結果。

微服務治理實踐:探尋業務的單點異常自愈能力 24

從以下拓撲圖中我們看到,流量不斷地訪問到 cartservice 服務上。

微服務治理實踐:探尋業務的單點異常自愈能力 25

開啟離群摘除後

離群摘除效果通過簡單的例子就看到了,當然可以通過 ARMS ( EDAS 監控系統)的監控可以明顯觀察到服務質量的提升。

微服務治理實踐:探尋業務的單點異常自愈能力 26

可以看到,在開啟了離群摘除的那個點只後,錯誤率從 50% 明顯下降。

微服務治理實踐:探尋業務的單點異常自愈能力 27

其中兩個小的起伏毛刺是因為,離群摘除一段時間後會重新嘗試訪問被摘除的 endPoint ,若依舊錯誤率高於閾值,繼續隔離,且間隔時間更長。

離群實例摘除具體控制邏輯

前面我們看到了,離群實力摘除對應用穩定性提高帶來的幫助,下面我們將具體分析離群實例摘除的控制邏輯,有助於您更好地理解其各種參數的意義,以及可以根據自己的應用情況,通過調整參數,配置出最合適自己的離群摘除策略。

對於 Dubbo/SpringCloud 框架:

  • 默認 QPS 下限為 1

只有當前某實例的調用 QPS 大於 1 才會開始離群實例摘除保護。

  • 默認錯誤率下限 50%

只有當前某實例的調用錯誤率高於 50% ,則係統會認為該服務端集群的當前某實例處於異常狀態。

  • 默認摘除實例比例上限 20%

若當前服務集群中,有大於20%的實例節點處於異常狀態,則係統只會摘除異常狀態的實例數佔集群總數的 50% 。

  • 異常類型

若異常類型為網絡異常,則係統僅僅把網絡異常的錯誤算進錯誤率統計中,忽略掉業務異常;反之,若選擇了網絡異常+ 業務異常則係統會將所有異常當成錯誤算進錯誤率統計中。

  • 關於恢復檢測單位時間(默認 30000ms 即 30s )與未恢復累計次數上限(默認 40 )的解釋

其中第一次摘除時長為0.5 分鐘,時間到了之後consumer 會繼續訪問該provider ,若該provider 服務質量依舊低下,則會繼續摘除,摘除時長隨著連續被摘除次數的增加線性遞增每次增加0.5 分鐘,每次最多摘除20 分鐘。當然,若繼續調用之後,服務質量恢復了,則會當成健康服務,下一次又出現異常導致服務質量低下問題時,會重新隔離 0.5 分鐘,並繼續上述規則。

  • 兜底

但是當客戶端調用的服務僅有 1 個實例提供服務提供則不會隔離這個實例。

若當前客戶端調用的服務實例大於 1 個,且當前離群摘除隔離比例計算出的實例數小於1,若服務端集群出現單點故障,則會摘除1個實例。

上面所有的實例可以理解為 endpoint(ip+port為緯度)

  • 通用最佳實踐

可以配置相對的錯誤率閾值(50%)與過低的摘除實例比例上限(10%),全鏈路開啟。

離群實例摘除技術細節

無侵入技術

無侵入方案即通過agent 技術來實現,一句話來說就是通過字節碼增強技術,運行時插入我們的代碼,改變應用的原有邏輯,可以理解為運行時AOP ,通過在Dubbo 的鏈路中插入Filter/Router ,在Spring Cloud 中增強LoadBalance 邏輯,來實現我們期望的路由控制邏輯。同時因為是 agent 增強的,再加上 Dubbo 各個版本的鏈路整體基本沒大的變化,Spring Cloud 模型的統一性,因此我們可以花少的代價將能力基本覆蓋到所有版本。

微服務治理實踐:探尋業務的單點異常自愈能力 28

Dubbo Agent 方案技術架構

對於用戶來說,無需改動一行代碼,一行配置,即可享受到穩定性增強的能力。

離群實例摘除技術

Outlier Detection 離群檢測

均是基於時間窗口的數據統計。兩種實現如下:

1、Dubbo 2.7 版本通過向鏈路中嵌入一個 MetricsFilter ,對於鏈路的每個 request/response 做打點處理,統計rt、調用成功與否、異常類型,並且已 endpoint(ip+port) 為 key 存儲。

2、在 Agent 底座中統計經過的 http 請求,通過 url、rt、狀態碼、異常類型等數據結果,統計最近時間窗口的數據(目前寫死 10 秒,暫時不透出)。

實時統計前 N 秒的調用信息,作為離群實例摘除動作的依據。

Outlier Ejection 離群摘除

Dubbo 基於 Dubbo Router 實現,對於調用的上游服務對應的所有 invokers 中,拉黑掉“不健康”的節點,同時記錄拉黑的信息。

微服務治理實踐:探尋業務的單點異常自愈能力 29

Dubbo-Router 控制邏輯

每次請求過來僅僅 check 一下並標記狀態,後台有專門兩個線程將標記的流量進行判斷是否進入隔離列表或從中剔除,修改拉黑信息等耗時操作,最大程度上保證請求的實時性。

Spring Cloud 基於 擴展 LoadBalace 實現,原理相似。

作者介紹

泮聖偉,花名十眠,中間件技術-微服務產品團隊研發工程師,負責 Dubbo / Spring Cloud 商業化產品開發相關工作,目前主要關注云原生、微服務等技術方向。

本文轉載自公眾號阿里巴巴中間件(ID:Aliware_2018)。

原文鏈接

https://mp.weixin.qq.com/s?__biz=MzU4NzU0MDIzOQ==&mid=2247488458&idx=3&sn=681e033fe57f036fc036034f1222398b&chksm=fdeb21aaca9ca8bcb1ff5a59f653fc1ed9670920fef03141810419b6da9461a7c910a9be3eac&scene=27#wechat_redirect