“延遲”指標如何作用於應用風險識別系統

隨着近幾年好大夫在線全面轉向微服務化,服務間調用複雜度急劇上升,故障難定位,技術債務難量化等相關問題隨之而來,如服務穩定性沒有統一衡量標準,服務容量配比是否合適等等。面對這些問題,好大夫在線基礎架構部積極探索落地全鏈路監控系統,主要包括鏈路診斷、容量監控、應用運行時健康畫像、實時告警及應用風險評估模型。本系列文章將從不同點切入,對好大夫在線落地全鏈路監控系統做一個介紹。

SRE(Site Reliability Engineering)在選擇指標SLI(Service Level Indicator)時,有四個黃金指標:延遲、流量、錯誤和飽和度(取自 Google SRE 的書籍)。

本期我們分析一下“延遲”指標如何作用於應用風險識別系統。

接口請求響應時間指示着應用單機峯值的QPS/TPS(在機器硬件配置相同的情況下)。

響應越快能併發處理的用戶請求也就越多,水平擴展時收益也越大,能減少獲客成本和機器成本。

請求慢有很多因素,從用戶角度來看,CDN回源、css/js/html渲染、網絡抖動、微服務接口粒度過細、局域網內部請求過多、機器硬件、依賴中間件等等都會影響請求的耗時,接下來我們從微服務局域網請求角度來分析。

應用出現慢接口有哪些表現和危害

  • 用戶的耐心是有限的,過慢的請求,會流失用戶,將用戶導向響應更快的競品;

  • php應用會導致php-fpm進程數被耗盡,單機處理能力大幅度下降,請求會被積壓,從而導致QPS/TPS下降,從而造成大量5xx錯誤;

  • 線程、進程阻塞,應用CPU負載因爲大量的context切換而飈高,導致應用響應越來越慢,甚至出現整個系統雪崩;

  • 由於大量線程/進程變慢,資源沒有及時釋放,導致各種連接池、中間件等連接數被耗盡,觸發過載保護,造成客戶端連接斷開,出現錯誤數據或者流程中斷;

什麼是慢接口(高延遲)?

慢接口:直觀來看就是接口慢。那接口耗時多少算慢呢?統計接口平均耗時大於某個閾值?一次響應慢就算嗎?

MessageQueue消費事件算嗎?定時任務裏面的方法執行慢算嗎?鏈路入口的慢接口又是如何判定的?基於這些疑問,我們根據經驗總結出慢接口的具體說明。

慢接口:當服務發起方單次RPC請求響應耗時大於當前應用95%請求耗時計數累加1,一個分析週期後,當前應用累計數排名TOPK的接口標記爲慢接口。

名詞解釋:

  • 單次請求,指的是一個請求週期內的響應耗時,比如入口鏈路耗時時間就是處理完這次請求的總耗時,任何請求都是上游方法請求下游方法的聚合;

  • 響應耗時 = 網絡傳輸耗時 + 應用處理耗時;

  • 每個應用職責不一樣,所在鏈路位置也不一樣,每個接口的耗時也不一樣,如果取平均耗時,由於長尾效應,分析的數據會失真,根據公司現狀分析,取95%百分位的接口響應時間記爲參考值;

  • 爲了避免網絡抖動和消除突發異常,我們採用累計數,調用量大的系統,看累加總數,調用量小的看請求佔比;

  • 一個分析週期,也是爲了消除抖動或者突發異常,目前我們採用一個小時爲分析週期;

爲什麼不用平均耗時

因爲通常對於延遲這個指標,我們不會直接做所有請求延遲的平均,因爲整個延遲的分佈也符合正態分佈,所以通常會以類似 “90% 請求的延遲 <= 80ms,或者 95% 請求的延遲 <=120ms ”這樣的方式來設定延遲 SLO(Service Level Objective) ,熟悉數理統計的同學應該知道,這個 90% 或 95% 我們稱之爲置信區間。

爲什麼每個應用的耗時指標設置不一樣

由於每個系統目前的機器分佈不均衡,調用量不相同,再加上歷史遺留代碼,改造成本也不一樣。爲了讓優化收益最大化,我們目前的規則是:按應用內接口橫向比較,取應用的p95耗時,再根據調用頻率,優先改排名前5的接口。優化是一個持續的過程,如果所有的應用都卡一個指標去優化,對那些老系統來說,短期內很難達成目標。先優化收益高的任務,有效果了,再優化新生成的任務,形成積極的正反饋。

慢接口優化的SLO

對延遲有一定了解之後,我們要怎麼做呢,也就是說我們的目標是什麼,延遲SLO(Service Level Objective) 優化目標,根據前面的分析,我們選擇爲應用接口訪問量大耗時大於當前應用95%請求的接口。這樣隨着優化的進行,p95 在逐漸下降 等p95達標後,可以優化p99,p99.9。

慢接口分析模型設計原理

a. 日誌存儲本地磁盤,基於Flume異步收集,推送到Kafka,日誌分析系統Snow(自研)訂閱Kafka消息進行流式處理,準實時分析;

b. 每條日誌攜帶的信息比較豐富,日誌分析系統採用Goroutine,按照目前定義的規則(如慢接口、異常事件、慢SQL、循環遠程調用等)進行併發分析;

c. 分析後,生成風險指標Metrics,判斷系統(Dolphin,見:好大夫在線監控系統答辯的60分鐘)週期性拉取Metrics存入Prometheus,通過預設的判定規則,觸發生成風險事件;

d. Task任務追蹤系統(自研)訂閱風險事件,生成可追蹤的任務後,下發到各個事業部的開發人員名下(直接生成對應的JIRA任務);

e. 測試同學管理自己部門相關的改進任務 -> 開發同學進行優化後上線 -> 測試同學線上驗證後關閉;

f. 整個流程從風險評估系統發起,到風險評估系統持續追蹤驗證,形成閉環;

宏觀架構如下:

我們如何找到慢接口?

我們將所有RPC請求的耗時,記錄到日誌(從發出請求 到 接收到請求 一個來回),輔助診斷工具“APM鏈路分析”,標註風險點。

任務是如何分配的

現在的風險評估模型,分析出來的任務都統一分配給了服務調用方,即分給了上游服務,我們認爲,應用負責人應該感知自己調用下游的健康狀態,對影響接口的因素要做到心中有數,如果鏈路跨度比較大, 比如 SystemA::methodA()調用SystemB::methodB()而SystemB::methodB()又調用了SystemC::methodC(),如果SystemC::methodC() 耗時超過100ms。這時候SystemA,SystemB,SystemC 都會生成任務,SystemA,SystemB負載人 推動SystemC負責人 去優化SystemC::methodC()。

任務追蹤系統狀態流轉

任務狀態流轉,從創建到關閉形成閉環:

任務優先級是如何劃分的

每個應用面臨的狀態不一樣,我們會優先選擇收益高的慢接口生成任務,應用方再結合調用量,和修改難易程度和項目排期自己合理安排。

系統抖動產生的任務如何處理

由於網絡抖動產生的臨時事件,或者依賴的框架異常,中間件突發異常生成的任務,可以直接靜默該任務,默認靜默一個月。

如何處理(優化策略)

有些應用歷史包袱比較重,調用跨度深,或者準備重構的均可以參考以下思路處理。

  • 不管是重構還是要優化,先分析鏈路調用關係,畫出時序圖,看看依賴是否合理,應該能發現一些可優化項;

  • 可以從產品層面思考一下,比如用戶提交訂單 在支付回調的過程中,給用戶顯示系統處理中頁面,讓同步請求轉成異步請求,減少依賴;

  • 分析慢接口的源頭,基於時間線分佈,查看是下游慢,還是自己當前應用處理邏輯慢;

  • 判斷是否依賴第三方接口,比如依賴微信,上傳/下載附件等,考考是否轉成異步處理;

  • 是否存在循環調用,頻繁的調用下游同一個方法除了徒勞浪費帶寬外沒啥意義;

  • 是否存在多次依賴,這時候主要考慮是否存在接口粒度過細的情況,如果依賴下游方法過多可以考慮是否需要聚合,當然根據單一職責設計原則,不要盲目聚合;

  • 調用跨度是否過大,RPC請求如果鏈路超過100次,假設每次請求耗時20ms,整條鏈路耗時也會超過2000ms;

  • 是否可以考慮併發請求,對於跨度大的,請求返回值不相互依賴的可以考慮併發請求;

  • 片段緩存被穿透,導致出現慢sql;

  • 腳本或者mq消費者,大量併發操作中間件,導致中間件過載防護,或超載 阻塞連接或拒絕連接,考慮延遲消費,減少併發量,定時任務分片,分時段執行;

  • get 等冪等請求,是否考慮增加片段緩存;

  • 對條件查詢數據庫的操作,是否可以轉化爲查詢主鍵;

  • 高頻調用中間件,是否可以調整爲批量操作,如批量發佈消息,和Redis交互 增加Pipline 減少網絡I/O;

  • 查看應用依賴是否合理,基礎應用不應該依賴高層應用,應用間的交互可以基於契約,高層應用也不直接依賴基礎應用,大家通過異步回調實現;

  • 代碼組織形式是否合理,可以基於設計模式 重構代碼,對接口做基準測試 對核心接口的QPS/TPS 要做到心中有數;

優化的誤區

  • 爲了減少併發,直接在程序中添加延遲因子,如sleep(n)。

慢接口模型會分析出這類接口,在優化和平時寫代碼的時候一定要注意,如果要延遲處理部分邏輯。可以考慮提交事務後觸發mq異步時間,如果是緩解併發問題可以利用加鎖,而不是隨機增加sleep時間。

  • 強制分離自己的業務領域職責,將自己的依賴的下游往上層拋,讓前臺代碼變的臃腫無法維護,並且沒有解決延遲慢的問題,只是將自己的問題拋給了上游調用方。

如controller層調用接單服務,優化後,本來SystemB::AddItemToFlow() 屬於SystemA::CreateOrder()的領域邏輯,拆到Controller後,雖然SystemA健康了,但整個鏈路耗時依然很大,並且破壞了SystemA的領域邏輯。

  • 盲目申請更大的緩存存儲空間,由於數據冷熱分佈不均,緩存命中率低,擴大緩存後命中率沒有顯著提升,只會徒增機器成本。

比如按用戶展示推薦文章列表,如果直接按入參設計片段緩存,由於userid不一樣,會大量申請緩存,命中率沒有提高,相當於每次還是直接查詢數據庫。

如何驗證優化效果,應用變好或變壞的信號? 關注應用風險評估系統的每週週報,關注風險評級指標,關注優化後的95線趨勢,以及優化後的任務是否被重新打開。

分析實戰

爲了方便大家持續跟進任務,我們開發任務跟進系統:“dany”,如下圖:

按事業部劃分應用歸屬,既可以查看每個事業部的風險趨勢,也可以選擇自己應用的任務列表,選擇慢接口類型,可以藉助APM鏈路分析,更方便的定位問題,測試也可以根據任務狀態推進開發優化。接下來我們具體來分析一個:

我們選一個循環調用的方法藉助APM診斷工具,分析一下:

APM 診斷工具,會給出分析報告,繪出上下游拓撲圖,繪出整個調用樹,繪出每個方法的時間線,給不健康的方法加上標籤 我們可以看出 這條鏈路中,存在3處雙向依賴,循環調用15組,調用跨度75,總耗時1.4s左右。爲了更好的用戶體驗,我們對界面做了很多支持動態配置的功能,比如篩選指定的風險點。

具體到1.4 這條鏈路中,我們可以看到總耗時147ms。

兩處循環調用1.4.1 和 1.4.2 都是調用getByHostIdAndType總耗時21ms,優化建議可以合併成一次請求。

雙向依賴有三處 1.4.4 -> 1.4.4.1 -> 1.4.4.2,我們可以看出層級關係,上游發起請求後,下游又回調了上游應用,這種交叉依賴,很容易形成依賴環,從而導致交叉故障,更早的到達故障臨界點。優化建議,聚合接口,上游一次性把參數傳給下游,剪掉雙向依賴。

從時間線外面可以判斷出,下游調用時間 一共消耗85ms, 去掉下游耗時,1.4 入口耗時62ms,也就是說,入口方法本身還存在優化空間。

其他的風險點都可以採用類似的方法進行分析,從而制定自己的優化方案。

分析步驟總結

  • 選擇應用,查看任務,分析調用鏈;
  • 分析鏈路每一個請求的時序圖;
  • 按風險進行過濾篩選;
  • 分析耗時時間線;
  • 找出主要可優化的點,然後去評估是否、如何去優化;

作者介紹:

方勇:好大夫在線系統開發工程師,專注於微服務、中間件的穩定性和可用性建設,整體負責好大夫風險評估系統的設計和搭建;

翟康奇:好大夫在線系統開發工程師,專注於分佈式任務調度,RabbitMQ高可用解決方案,負責全鏈路應用風險分析建模;

劉偉:好大夫在線系統開發工程師,主要負責統一推送平臺,中間件運行時指標的監控挖掘及調優,主導RabbitMQ多集羣管理平臺建設。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章