故障常見緣由歸類分析及預防和應對措施

每一次故障都是一次寶貴的學習機會。html

引語

故障是開發者頭上懸着的一把劍。俗語曰:no zuo no die. 但是開發者很難作到 no zuo. 如何在 zuo 的時候防止 die 呢 ?程序員

知己知彼才能百戰不殆。要避免故障,就須要對故障有一個相對深刻的理解。算法

故障,通常是指一段時間內較爲密集的問題發生致使了必定的負面影響。業務量小的極少影響面的問題不算故障,不然就會混淆真正的故障,致使受限資源投入分配不合理,影響關鍵問題的解決進度;零星的非密集的問題可能不是故障,由於那多是小几率事件觸發了潛在BUG,須要解決,但定爲故障有點勉強。數據庫

要避免故障,首先須要深刻了解故障發生的緣由。如下內容來自於對多起故障的分析、歸類和總結。

後端

分析方法

拿到一個故障,如何分析它 ? 如何從中學到最大的收穫 ? 如何給它進行歸類呢 ?安全

首先,要確立分析目標。 我重點關注的是故障發生的主要緣由及預防措施,而不是現象及處理過程和時長。所以,能夠概覽故障現象描述、處理過程及時長、次要因素等,除非其中有重要價值的內容;網絡

其次,軟件的根本任務是處理數據。 故障的本質就是數據處理出錯了。 或者是 數據處理成非預期的結果,或者是數據處理延遲,或者數據展現有問題,或者兼而有之 。 所以,數據是故障分析的一個重要關注視角。併發

再次,處理能夠抽象爲算法。 處理有問題,或者是選用了錯誤的算法,或者是算法裏新增或修改的部分對某些場景不適配,破壞了原有約定 ,或者兼而有之。所以,算法是故障分析的另外一個重要關注視角。負載均衡

最後,若是一個故障的緣由有一個明確的斷定,就能夠爲之定名;若是它不屬於已有的任何一個定名,就要新建一個定名,將其放入其中。運維


故障緣由

多發源

故障多發源,是指發生故障的最多見緣由。謹防這幾種情形,能夠預防大部分的故障可能性。

核心流程出錯

核心流程的某個環節出問題,致使總體流程失敗,或者部分業務場景的總體流程失敗,都會致使密集問題發生。一般是在主流程中添加了一段代碼,而這段代碼沒有考慮到某個場景或者健壯性不佳,影響了總體;在測試的時候,只驗證了改動點部分,沒有迴歸核心流程,或者回歸了核心流程,卻遺漏了某個場景的迴歸。

預防措施:

  1. 評估改動點! 很是重要!哪怕只有一行,只要在主流程中,都要仔細評估其影響範圍。在主流程中添加的代碼越長,越要警戒。
  2. 增長必要的 try-catch 。若是增長的代碼只有局部影響,能夠添加必要的 try-catch,防止未預料的情形的處理異常影響總體流程。
  3. 最好不要輕易改動影響全局的通用方法和配置(影響面和迴歸面很是大); 儘量只新增而不是修改。
  4. 覆蓋全面的核心流程的測試用例,每次發佈都須要迴歸經過。
  5. 有風險性的改動,增長開關。一旦出錯,當即關閉改動。

真實案例:

場景遺漏

業務會逐漸發展成龐然大物,隨着人員的流動,不少業務知識和場景會逐漸被淡忘。新進的同窗若是沒有充分評估到各類場景,就很容易覺得遺漏某個場景,致使問題。要解決這種緣由,是比較棘手的。

預防措施:

  1. 沉澱業務文檔和業務場景。
  2. 有全景圖意識,不限於眼前的一畝三分地。
  3. 有熟悉業務的同窗進行方案評估和 CodeReview 。

缺少健壯性

實現服務以後,健壯性是保證服務可以平穩運行、正確應對錯誤和異常的第一道關卡,也是合格程序員的必備代碼素養之一。

健壯性不佳,很容易致使因爲未預料的局部細節、髒數據、局部調用失敗影響總體的流程和展現。

預防措施:

  1. 思考錯誤和異常,多多益善。
  2. 善用 try-catch 保駕護航。
  3. 使用空字符串、空列表替代 null 。
  4. 異常分支的測試覆蓋。

真實案例:

  • 因爲一個 null 值致使整個訂單列表加載失敗。
  • 因爲一個次要的依賴出錯致使整個詳情頁加載失敗。
  • 異常分支的代碼有問題,但沒有測試;當流程走到異常分支代碼,任務直接跪掉,反覆重啓和跪掉。

瞬時大流量

瞬時大流量是形成故障的一大殺手。 瞬時大流量,會致使機器資源短缺,CPU 飆升或內存爆滿或網卡、鏈接數打滿,直接影響總體服務的穩定性。

對於消息處理應用來講,瞬時大流量會致使消息處理延遲,業務狀態流轉滯後,影響後續環節;對於非消息處理應用來講,則會致使任務處理阻塞,接口響應變慢或不響應。

低性能、低吞吐量在面臨持續多個較大業務量的衝擊時,很容易出現阻塞、延遲;若是應用無限流,或限流失效,或限流不夠精確,均可能難以抵擋大流量的侵襲。

預防措施:

  1. 集羣環境:保證集羣各機器或 Region 的負載均衡;
  2. 單機環境:有針對性地限流、限速和限數,並嚴格測試和驗證。
  3. 壓測演練。容器化後的壓測。
  4. 減小或消除太重的鎖邏輯。
  5. 大流量預警和感知。好比線程池隊列阻塞預警,計算出的大數據集處理的預警 等。
  6. 批量調用替換循環單個調用;O(nlogn) 算法;減小沒必要要的訪問和服務依賴。

真實案例:

極端狀況

極端狀況是指,一些很罕見的事件的發生挑戰了系統的某個局部極限,致使系統出了問題。

好比說,一個訂單內的商品種數一般不會超過 10 ,但商家或買家刷單,致使大量含有 50 多個商品的訂單,而後密集導出,就會致使應用 FullGC 嚴重,引發接口響應超時或任務沒法進行下去。

預防措施:

  1. 思考極端情形及影響;
  2. 提早作好極端情形測試和設計方案。

真實案例:

依賴失敗

依賴失敗有以下情形:

  1. 所依賴的服務、配置或變量不存在或處於不合適的版本,致使應用啓動失敗,或者啓動後的服務不能正常運行;
  2. 所依賴的基礎服務不穩定出現大量報錯時,會致使依賴它的高頻應用也出現大量報錯,致使雪崩效應。
  3. 配置或服務循環依賴,致使死循環。

預防措施:

  1. 當一個項目發佈涉及多個系統或許多細節時,就須要編寫發佈文檔,仔細指定發佈配置和順序,保證應用依賴的正確性。在具體發佈時,則要嚴格執行發佈文檔裏指定的檢查點清單和發佈順序。檢查依賴項:API 版本、Jar 版本、依賴服務、配置項、DB 字段。
  2. 自動降級。嚴格控制超時,隔離或去掉沒必要要的弱依賴。
  3. 制定明確的依賴原則,上游依賴基礎,避免循環依賴。
  4. 先後端對接口約定的返回值及格式溝通達成一致。

資損

資產是客戶很是敏感的私有產權。發生資損時,一般是最高故障級別。

資損通常發生在:1. 直接資損: 系統處理未考慮冪等,致使重複消息屢次處理;2. 業務方根據基礎服務方的某些字段進行資金業務處理,而字段返回值有誤,致使少算或多算。3. 誘導性資損,因爲某些展現信息,誘導用戶作出某種難以追回的行爲,好比已發貨訂單展現爲待發貨;

預防措施:

  1. 直接處理資金業務,注意冪等處理;
  2. 有依賴狀態的資金業務處理?
  3. 消除誘導性信息;
  4. 對資金計算敏感,尤爲注意邊界值處理,避免 +1 或 -1 致使問題。

新舊遷移出錯

多發生於技術重構優化的時候。好比舊的領域模型遷移新模型、舊的技術棧遷移新技術棧、舊的頁面遷移新頁面。作技術改造,側重點每每在於新服務的測試,而容易忽略老服務的測試兼容。

新舊遷移存在一個權衡:完全仍是減小出錯。更爲完全的遷移,出錯和故障機率會更大,但新系統會更加清爽;向老系統做一些妥協,能夠減小一些出錯和故障機率,但新系統會帶着老系統的包袱前行,後續依然會出問題。

預防措施:

  1. 分流。 分流能夠確保新服務上線以後的影響面逐漸擴大,即便有未考慮的點,也會將影響面控制在最小範圍。
  2. 充分測試,事先評估好測試用例並嚴格執行。
  3. 舊接口遷移到新接口時,返回值的結構和值約定最好一致,確保 新對新,老對老,避免「新對老」的不兼容致使問題。
  4. 老的頁面和功能要回歸全面,避免重要場景遺漏。
  5. 新的和老的代碼改動分開 CR ,分批 CR 。

老代碼

不能否認,老代碼在企業初創期曾立下汗馬功勞。但是,隨着時間推移,業務量愈來愈大,複雜度也在快速增長,不少老代碼的簡單處理就逐漸變成了「定時炸彈」,冷不防讓地震一震,讓人抖一抖。

預防措施: 按期梳理和清除。

真實案例:

數據泄露

數據安全性愈來愈成爲企業的重要關注點。對於 SaaS 來講,要保證各個租戶的數據和操做互不影響,不能看到和操做未受權的數據。

預防措施: 1. 敏感數據脫敏; 2. 避免覆蓋; 3. 權限控制; 4. XSS 安全問題。

真實案例:

設備及網絡

設備及網絡屬於互聯網的基礎設施,位於最底層,一旦出現問題,影響面也是巨大的。當設備老舊出現硬件故障或宕機,或者網絡抖動或忽然斷開,也是很容易致使大面積失敗。

預防措施:

  1. 按期檢查和更換老舊設備。花點錢更換老舊設備,比宕機出現問題花費時間、精力和金錢補償,要划算得多。
  2. 備用鏈路和機房。
  3. 避免單點故障。


操做不當

操做不當主要有以下情形:

  1. 兩種操做同時進行,發生衝突致使出錯;
  2. 代碼合併衝突解決不當;
  3. 操做不規範,引起系統處理失常或失控。

預防措施:

  1. 代碼合併衝突解決,雙方確認。
  2. 同時更改系統配置,須要溝通協調順序,避免併發。
  3. 批量數據修復方案要仔細 CodeReview , 當作正式發佈處理;
  4. 批量數據修復在業務低峯期進行,除非是緊急修復。
  5. 變動操做的工具自動化,審批流程。

真實案例:

  • 在業務量比較大的情形下,對錶進行 truncate 操做可能會致使數據庫 hang 住。

其餘緣由

資源未隔離

底層集羣未能隔離不一樣業務的資源, A 業務的大流量致使集羣機器資源被打滿,間接影響了 B 業務。

預防措施:

  • 規劃和實現資源隔離策略:重要業務和次要業務的資源進行隔離;不一樣業務的資源進行隔離。

髒數據

因爲髒數據缺少總體的關聯性約束,應用讀取到髒數據,容易出錯;若是應用有一連串的邏輯處理,可能生成更多的髒數據,引出更大的麻煩。

預防措施:

  1. 檢測和消除髒數據。
  2. 避免在線上造測試數據。


故障處理

發生故障時,第一反應不是當即排查緣由,而是當即止損,將影響面最小化。

  • 若能肯定是發佈致使,當即回滾發佈。 回滾發佈後,再仔細排查緣由。
  • 及時同步進度,讓關注方知悉;
  • 創建快速同步機制,預防小問題演變成大故障。

爲了更好地減小故障的可能性,還須要事先作好故障應急預案。

  • 梳理底層的強弱依賴,肯定強依賴不可用時致使的影響面;
  • 當強依賴不可用時,可以快速恢復的方案,將影響面下降到最小。
  • 故障演練。模擬大流量、極端狀況和故障情形發生,檢測應急預案是否生效和快速恢復。

根因探討

故障層出不窮,現象眼花繚亂,究竟從何處來,去往何處呢 ? 是否有根本規律可循 ?

事實上,絕大多數的軟件故障都是具有內在的邏輯關聯的。從基礎邏輯關聯來推理,能夠推斷出不少本能夠預防和避免的問題。與正常流程相比,故障自己也是一種路徑,產生出特定的數據集,只是這些數據集及引起的現象是不符合人們的預期的。如下是部分基礎邏輯關聯分析:

  1. 依賴問題。 一個功能會依賴某個字段、配置、校驗、接口約定等;當字段或配置變動不合理,或語義發生變化時,會致使問題; 兩個業務 A 和 B 均依賴同一段代碼 c ,根據對 A 的某個需求在代碼 c 修改了一些邏輯 ,影響了 B ,結果致使 B 出了問題;在原有流程中多了沒必要要的校驗或少了必要的校驗,會致使問題;原來依賴格式 A,遷移新服務後變成了不兼容的格式 B。 絕大部分功能性問題均可以歸結爲評估影響面不許確。
  2. 流量問題。一般是大流量或極端情形致使,超過了系統可以承載的閾值。對系統的閾值壓測摸底並提早預估好容量,輔以通過嚴格測試和驗證過的限流、限速等。
  3. 環境問題。依賴的環境假設出現問題,致使依賴鏈路阻斷,從而引起各類問題。須要在運維層面保證環境的高可用高可靠性,避免單點。

小結

故障,是每一個開發者乃至企業法人都不肯意經歷的事情。但是,每一次故障,都蘊含着不一樣形式的疏忽、未知、真理,正向思考,實際上是一次很是珍貴的學習機會。故障,也會引導人抵達更深刻的境地,去理解事情的本質與關聯。正視故障,從故障裏學習真知,預防和避免故障,乃是更佳的姿式。

要預防故障:

  • 第一是細心。多個心眼,準確評估影響面,兼顧考慮老業務老功能的迴歸, 仔細檢查依賴項,保證返回約定的一致性,規範執行;
  • 設計和實現要考慮健壯性、大流量和極端情形,避免低性能。
  • 有針對性避免安全性和資損問題。
  • 設置嚴密的監控報警,在問題的萌芽期掐滅。
相關文章
相關標籤/搜索