天天看點

混沌演練狀态下,如何降低應用的 MTTR | 京東雲技術團隊

作者:京東雲開發者

在企業業務領域,錦禮是針對福利、營銷、激勵等員工采購場景的一站式解決方案,包含面向員工、會員等彈性激勵 SAAS 平台。由于其直接面向公司全體員工,其服務的高可用尤其重要,本文将介紹錦禮商城大促前夕,通過混沌工程實戰演習,降低應用的 MTTR。

MTTR(平均恢複時間)是從産品或系統故障中恢複所需的平均時間。 這包括整個中斷時間——從系統或産品出現故障到其恢複完全運作為止。

如何在混沌演練的場景中降低應用的 MTTR,必須需要根據監控定位,然後人工進行回報進行處理嗎?是否可以自動化,是否有方案可以降低混沌演練過程中的影響?以此達到快速止血,進一步提高系統的穩定性。

本篇文章将根據一些思考和實踐來解答以上問題。

故障無處不在,而且無法避免。

我們将從主控端重新開機問題以及底層服務混沌演練的排查與舉措說起。

背景

【用戶端視角】:出現大量接口(包括提單)逾時報錯、可用率跳點,部分客戶命中,産生客訴。

通過定位發現大促備戰前期主控端重新開機及底層服務混沌演練原因,較長時間影響我側系統可用率及性能。尤其是核心接口的部署應用,會大範圍的影響到多個接口的可用率,進一步影響采購端客戶的體驗問題。

特别在 TOB 領域,本身就存在大客戶的口碑效應,如果恰好頭部客戶碰到該問題,那麼極易被放大和激化。

混沌演練狀态下,如何降低應用的 MTTR | 京東雲技術團隊

臨時舉措

一方面協同運維組确認主控端重新開機未及時通知的情況,另一方面與底層服務提供者同步演練影響,建議其遵守演練原則最小化爆炸半徑,控制影響範圍,保證演練是可控的。

除了以上協同外部的情況外,我們内部也産生了思考,首先情況故障本身就是不可控的,無論主控端還是混沌演練,真實場景也是有機率發生的(并且已發生)。那麼我們隻能通過監控定位,然後手動摘除機器或者通知服務提供者處理嗎?是否可以自動化,是否有方案可以降低影響?以此達到快速止血,進一步提高系統的穩定性。

長期方案——JSF 中間件能力實踐

既然無法避免故障,那麼就擁抱故障,通過一些技術手段來建構擷取應用故障的能力,進而保證應用的高可用。

由于内部的調用 90+%為(JSF)RPC 調用,是以我們還是把目光放到了 JSF 中間件的容錯能力上,以下主要介紹通過 JSF 中間件的逾時與重試、自适應負載均衡、服務熔斷來進行故障轉移的理論與實踐。

實踐是檢驗真理的唯一标準。

關于逾時和重試

實際開發過程中,相信大家也見過太多由于逾時未設定、設定有誤導緻的故障。當逾時未設定或者設定不合理,會導緻請求響應變慢,慢請求的不斷累計疊加,就會引起連鎖反應,甚至産生應用雪崩。

不僅我們自身的服務,還有外部的依賴服務,不僅 HTTP 服務,還是中間件服務,都應該設定合理的逾時重試政策,并且重視起來。

首先讀寫服務的逾時重試政策也是大不相同的,讀服務天生适合重試(如設定合理逾時時間後重試兩次),但是寫服務大多是不能重試的,不過如果均是幂等設計,也是可以的。

另外設定調用方的逾時時間之前,需要先了解清楚依賴服務的 TP99 響應時間是多少(如果依賴服務性能波動大,也可以看 TP95),調用方的逾時時間可以在此基礎上加 50%Buff。當然服務的響應時間并不是恒定的,在某些長尾條件下可能需要更多的計算時間,是以為了有足夠的時間等待這種長尾請求響應,我們需要把逾時設定足夠合理。

最後重試次數不宜太多(高并發時可能引發一系列問題(一般 2 次,最多 3 次),雖然重試次數越大,服務可用性越高,但是高并發情況下會導緻多倍的請求流量,類似模拟 DDOS 攻擊,嚴重情況下甚至于加速故障的連鎖發生。是以逾時重試最好是和熔斷、快速失敗等機制配合使用,效果更佳,這個後面會提到。

除了引入手段,重要的是驗證手段的有效性。

模拟場景(後續另兩個手段也是用該場景)

方案:采用故障注入(50%機器網絡延遲 3000-5000ms)的方式模拟類似場景,并驗證。

機器部署如下:

混沌演練狀态下,如何降低應用的 MTTR | 京東雲技術團隊

【注意】

網絡場景不支援如下情形:

1、應用容器所在機房:lf04, lf05, lf07, ht01, ht02, ht05, ht07, htmysql, lfmysql02, yn02, hk02, hk03

2、實體機的核心版本:2.6x, 3.8x, 3.10x

正常情況(未注入故障)

混沌演練狀态下,如何降低應用的 MTTR | 京東雲技術團隊
混沌演練狀态下,如何降低應用的 MTTR | 京東雲技術團隊

注入故障——逾時設定不合理情況下(逾時 2000ms,重試 2)

混沌演練狀态下,如何降低應用的 MTTR | 京東雲技術團隊
混沌演練狀态下,如何降低應用的 MTTR | 京東雲技術團隊

注入故障——逾時設定合理情況下(逾時 10ms,重試 2)

該接口 TP99 在 6ms,設定逾時 10ms,重試 2。即:jsf:methodname="queryActivityConfig"timeout="10"retries="2"/

混沌演練狀态下,如何降低應用的 MTTR | 京東雲技術團隊
混沌演練狀态下,如何降低應用的 MTTR | 京東雲技術團隊

逾時重試小結

通過合理的逾時重試,整體請求平穩,重試後的故障轉移,大幅提升接口可用率。

逾時重試補充

在接口次元拆分不合理的情況下,我們可以更細粒度的使用方法次元的逾時重試配置,不過這裡有一個注意項 JSF 目前注解方式不支援方法次元的逾時重試設定,僅支援接口次元,如已使用注解類,可進行遷移 XML 方式進行配置使用,

關于自适應負載均衡

對于 shortestresponse 自适應負載均衡設計目的是解決在 provider 節點能力不均的場景下,讓處理能力較弱的 provider 少接受些流量,不會因個别性能較差的 provider 影響到 consumer 整體調用的請求耗時和可用率。

能者多勞拙者閑,智者多憂愚者無所慮。

但是該政策下也是存在一些問題的:

  1. 流量的過度集中高性能執行個體,服務提供者的單機限流或成為瓶頸。
  2. response 的時間長短有時也并不能代表機器的吞吐能力。
  3. 大多數的場景下,不同 provider 的 response 時長在沒有非常明顯的差別時,shortestresponse 同 random(随機)。

現有的 shortestresponse 的實作機制,類似 P2C(Power of Two Choice)算法,不過計算方式不是采用目前正在處理的連接配接數,而是預設随機選擇兩個服務提供者參與最快響應比較計算,即:統計請求每個 provider 的請求耗時、通路量、異常量、請求并發數,比較平均響應時間 * 目前請求數,用于最快響應負載計算。選取優勝者來避免羊群效應。以此自适應的衡量 provider 端機器的吞吐能力,然後将流量盡可能配置設定到吞吐能力高的機器上,提高系統整體服務的性能。

<jsf:consumer id="activityConfigService"
                  interface="com.jd.ka.b2b2c.shop.sdk.service.ActivityConfigService"
                  alias="${jsf.activityConfigService.alias}" timeout = "3000" filter="jsfLogFilter,jsfSwitchFilter"
                  loadbalance="shortestresponse">
        <jsf:method name="queryActivityConfig" timeout="10" retries="2"/>
    </jsf:consumer>

           

複制代碼

注入故障(設定自适應負載均衡)

混沌演練狀态下,如何降低應用的 MTTR | 京東雲技術團隊
混沌演練狀态下,如何降低應用的 MTTR | 京東雲技術團隊
混沌演練狀态下,如何降低應用的 MTTR | 京東雲技術團隊

自适應負載均衡小結

通過引入自适應負載均衡,從接口最初調用就開始了”能者多勞“模式,選舉出的機器承載着更高的流量,故障注入後,接口可用率短時間視窗消失,變成可用率跳點,進一步保障了服務的高可用及性能。

關于服務熔斷

當電路發生短路或嚴重過載時,熔斷器中的熔斷體将自動熔斷,對電路進行保護。避免對裝置産生重大影響,甚至火災。

服務熔斷是面向不穩定服務場景的一種鍊路保護機制。

其背後的基本思想非常簡單,将受保護的函數調用包裝在熔斷對象中,該對象會監視故障。當調用鍊路的某個服務不可用或者響應時間太長導緻故障達到設定門檻值時,會進行服務熔斷,熔斷視窗内不再有該節點服務的調用,以此來最大限度避免下遊服務不穩定對上遊服務帶來的影響。

<!-- 服務熔斷政策配置 -->
<jsf:reduceCircuitBreakerStrategy id="demoReduceCircuitBreakerStrategy"
    enable="true"   <!-- 熔斷政策是否開啟 -->
    rollingStatsTime="1000" <!-- 熔斷器名額采樣滾動視窗時長,機關 ms,預設 5000ms -->
    triggerOpenMinRequestCount="10" <!-- 機關時間内觸發熔斷的最小通路量,預設 20 -->
    triggerOpenErrorCount="0"   <!-- 機關時間内的請求異常數達到閥值,預設 0,小于等于0 代表不通過異常數判斷是否開啟熔斷  -->
    triggerOpenErrorPercentage="50" <!-- 機關時間内的請求異常比例達到閥值,預設 50,即 預設 50% 錯誤率  -->
    <!-- triggerOpenSlowRT="0" 判定請求為慢調用的請求耗時,機關 ms,請求耗時超過 triggerOpenSlowRT 則認為是慢調用 (預設為 0,即預設不判定)-->
    <!-- triggerOpenSlowRequestPercentage="0"  采樣滾動周期内觸發熔斷的慢調用率(預設為 0,即預設不觸發慢調用熔斷 -->
    openedDuration="10000"   <!-- 熔斷開啟狀态持續時間,機關 ms,預設  5000ms -->
    halfOpenPassRequestPercentage="30"  <!-- 半閉合狀态,機關時間内放行流量百分比,預設 40-->
    halfOpenedDuration="3000"   <!-- 半閉合狀态持續時間設定,需要大于等于 rollingStatsTime ,預設為 rollingStatsTime  -->
    <!-- failBackType="FAIL_BACK_EXCEPTION" failBack政策, 取值:FAIL_BACK_EXCEPTION抛出異常、FAIL_BACK_NULL傳回null、FAIL_BACK_CUSTOM配置自定義政策,配合 failBackRef 屬性 -->
    <!-- failBackRef="ref" 如果 failBackStrategy 配置為 FAIL_BACK_CUSTOM 則必填,使用者自定義的failback政策com.jd.jsf.gd.circuitbreaker.failback.FailBack<Invocation> 接口實作類 -->
/>


<jsf:consumerid="activityConfigService"interface="com.jd.ka.b2b2c.shop.sdk.service.ActivityConfigService"
                alias="${consumer.alias.com.jd.ka.b2b2c.shop.sdk.service.ActivityConfigService}" timeout="2000"check="false"
                serialization="hessian"loadbalance="shortestresponse"
                connCircuitBreakerStrategy="demoCircuitBreakerStrategy">
      <jsf:methodname="queryActivityConfig"timeout="10"retries="2"/>
</jsf:consumer>

           

複制代碼

這裡來了一個小插曲,由于 JSF 本身的心跳機制,檢測故障後,自動(30s 檢測一次,三次均異常則摘除)摘除了對應的機器,我們自身設定的熔斷機制并不明顯,是以重新設定故障(網絡延遲 800-1500ms)進行重新演練。

注入故障(服務熔斷)

混沌演練狀态下,如何降低應用的 MTTR | 京東雲技術團隊

服務熔斷小結

從可用率上看,确實在視窗内會關閉對異常機器節點的通路,不過由于并沒有實作 failback 政策以及熔斷開啟視窗時間較短,可用率還是會在視窗打開後,直接傳回了調用失敗資訊,是以影響了可用率。是以相比于熔斷後失敗,最好的方式是配合服務降級能力,通過調用預先設定好的服務降級邏輯,以降級邏輯的結果作為最終調用結果,以更優雅的傳回給服務調用方。

服務熔斷補充

  1. 集團已搭建了統一的熔斷元件,并且在泰山上建立了對應的平台能力。如果團隊需要引入熔斷能力,可以直接接入使用,避免重複建設。詳情見:http://taishan.jd.com/flowControl/limitIndex
> 一種機制可能會擊敗另一種機制。

           

其實為了增強系統的彈性和魯棒性,以應對各種故障和不可預測的情況,在分布式系統中,通常會設計成能夠部分故障(partially fail),即使不能滿足全量客戶,但是仍然可以向某些客戶提供服務。但是熔斷旨在将部分故障轉化為完全故障,以此防止故障進一步擴散。是以服務熔斷和分布式系統的設計原則中存在一種互相制約的關系,是以,在使用前,要進行仔細的分析和思考,以及後續的調優。

結論

能力隻是手段,穩定性才是目的。

無論采用什麼手段,進行穩定性建設,我們需要時刻思考的是如何在業務需求和穩定性建設中尋找平衡,以建設支援業務長期增長的高可用架構。

本次就寫到這,如有問題,歡迎交流。希望文章中的一些經驗,給大家帶來一些收獲,或者說,大家不妨思考一下你們會采用何種技術方案和手段來解決類似問題。歡迎留言交流,也希望能和更多志同道合的夥伴溝通交流。

參考文檔

外部文檔

  • The power of two random choices : https://brooker.co.za/blog/2012/01/17/two-random.html
  • 負載均衡:https://cn.dubbo.apache.org/zh-cn/overview/core-features/load-balance/#shortestresponse

作者:京東零售 李孟冬

内容來源:京東雲開發者社群

繼續閱讀