隨着阿里技術的發展,咱們的技術系統愈來愈成爲社會的基礎設施,對於這些系統的可靠性要求也就愈來愈高。可是實際上不少的基礎的產品和系統確仍然會出現一些穩定性問題,那麼如何才能構建可靠的系統呢?是否是制定很是嚴格而細緻的規則就能夠作出可靠的系統呢?數據庫
在回答這個問題以前,咱們先來看看對於系統可靠性要求很是高的航空業是怎麼作的?美國的FAA是在航空安全領域事實上的權威,爲了保證航空器的安全,FAA制訂了很是詳細而複雜的航空器認證規則,而這些規則是否就保證了航空器的安全了呢?緩存
讓咱們來了解一下最近的兩起空難:安全
2019年3月10日,埃塞俄比亞航空ET302航班在起飛六分鐘後墜毀,飛機上載有149名乘客和8名機組人員。ide
2018年10月29日,印尼獅航JT610航班在起飛後約十分鐘墜毀,飛機上載有181名乘客,和8名機組人員。測試
幾百條鮮活生命的消逝,這是多麼嚴重的後果啊!到底是什麼緣由致使了這樣的災難呢?google
這兩起空難的共同點是都是波音的737MAX機型,而且都是在起飛後不久發生的空難。那麼這個背後的緣由是什麼呢?雖然官方的調查尚未結束,可是民間的分析指向了同一個緣由,那就是這款機型的設計問題。spa
上圖展現了Boeing 737 MAX的CFM LEAP引擎,值得注意的是和通常民航飛機不一樣的是,引擎的上沿和機翼平面幾乎齊平。爲何會這麼設計呢?這是由於波音737系列最先是上世紀60年代設計的,當時的引擎的直徑小不少,外形更加細長,而機翼的高度是和引擎直徑相匹配的。可是隨着技術的發展,更新更省油的引擎直徑變得愈來愈大,這時候原來的機翼高度沒法知足更大直徑引擎的安裝空間,要想調整機翼的高度則須要改變起落架的設計,改變起落架的設計則須要改變起落架收起時相關機體位置的設計,而機體設計的變化會帶來更多的變化從而會被FAA認爲是一款全新型號的飛機,而全新型號的飛機則須要經歷完整的FAA認證流程,會帶來巨大的時間和經濟成本。爲了不這樣的成本波音選擇了將引擎前移而且提高高度,可是這樣帶來了另一個問題,因爲空氣動力學方面的緣由,飛機會變得靜不穩定,特別是在起飛階段,引擎的推力會致使飛機迎角太高進入危險的失速狀態。爲了迴避這個問題,波音引入了一個自動控制程序MCAS,經過讀取迎角傳感器的數據判斷飛機是否迎角太高,若是太高的話自動控制飛機下降迎角,從而保證飛機的安全。設計
那麼這麼一套保證飛行安全的系統和空難有什麼關係呢?事實上MCAS系統工做得很是好,根據波音本身的統計,Boeing 737 MAX系列已經完成了數十萬次的安全起降。可是問題在於當傳感器工做不正常時,MCAS有可能會根據錯誤的迎角數據作出錯誤的判斷和動做,也就是在不該該下降迎角的時候下降迎角,致使飛機直衝地面。調試
回到咱們的工做中,前不久咱們碰到了一塊兒系統故障,其過程有必定典型的意義,爲了描述方面,這裏隱去一些具體細節,簡單說一下故障的過程。
開始的時候,因爲某些緣由致使緩存命中率有所降低,而緩存命中率降低致使了數據庫load升高,而數據庫load升高以及可能的慢SQL致使了部分請求在獲取DB connection的時候超時,從而引起了exception。當exception發生的時候,爲了保證系統的可用性,系統邏輯進入了一段兜底邏輯,而這段兜底邏輯在特定的條件下產生了錯誤的返回,從而致使線上髒數據,而這些髒數據帶來了業務資損和大量的人工訂正數據的成本。事務
這個故障處理的過程並非重點因此再也不贅述,咱們要問的是爲何一個簡單的exception會致使這麼嚴重的後果呢?
若是咱們仔細去觀察上述兩個事例,咱們會發現其中有以下幾個共同點:
換句話說,系統設計者在嘗試用很是簡單的邏輯去解決一個實際上覆雜的問題,雖然實際上並無徹底解決問題,可是由於這樣的邏輯可以經過大量的測試(或者合規檢查),因此係統設計者「假定」問題獲得瞭解決,從而放心地應用到了生產環境。
那麼一個很是複雜的問題是否真的可以經過一個簡單巧妙的辦法解決嗎?
在系統設計領域,咱們一般會把問題的複雜性分爲兩類,分別是偶得複雜性,實質複雜性。
偶得複雜性 Accidental Complexity
所謂偶得複雜性是指由開發者本身在嘗試解決問題時引入的複雜性挑戰,通常而言是由解決問題的方法和手段帶來的,對於特定的問題,不一樣的方法會帶來不一樣的偶得複雜性。
實質複雜性 Essential Complexity
所謂實質複雜性是由事務自己所決定的,和解決方法無關。
對於偶得複雜性,經過變換解決辦法是有可能用簡單的辦法來解決的,可是對於實質複雜性,咱們是沒法經過改變手段來解決的,而必須採用相應複雜的方法來解決問題。換句話說對於實質複雜的問題,不要期望有銀彈。
上述事例中,實際的環境和問題是存在比較大的實質複雜性的,然而咱們卻試圖經過一些很是簡單的邏輯去解決問題,從而帶來了嚴重的後果。
那麼要想防止這類問題,設計高可靠的系統要怎麼作呢?
這裏我想介紹一條反直覺的軟件設計原則,快速失敗(Fail Fast):
In systems design, a fail-fast system is one which immediately reports at its interface any condition that is likely to indicate a failure.
這是一條反直覺的原則,大部分人據說這條原則的第一反應是這樣不是讓系統變得更加脆弱了嗎?
實際上並非,緣由在於咱們不能停留在某一次的失敗(failure),而是須要觀察完整的過程,以下圖所示:
當問題發生時,系統當即中止工做,由人工介入找到並以合理的方式解決根本的問題,而後系統恢復運做。經過這樣的選擇,咱們就可以更早更容易地暴露問題,每當系統發生問題以後,真正的根因會更快得以解決,因此最後咱們就能獲得一個更加可靠的系統。
須要說明的是,快速失敗不是說系統設計不處理任何問題處處失敗,而只是在面對essential complexity的時候,不要嘗試用一個簡單粗暴的方案去解決,要麼就用一套合理的機制設計去解決它,要麼就fail fast把控制權交給系統上層決策,一般來講最終可能會迴歸到人,由人來分析和處理問題。
根據實際的工做經驗,我還發現一個有意思的現象,不少系統的設計者每每高估一些能輕易想到的問題的嚴重性,而低估那些想不到的問題的嚴重性。上述的軟件系統故障的例子中,若是異常往外拋出,問題的後果可能僅僅是某些操做人員的部分操做失敗,用戶可能會重試,也可能會開工單把問題反饋上來。只要咱們處理工單的同窗及時響應而且解決根本問題,這個問題就不會演變成一個比較嚴重的問題。可是不拋出異常經過備用邏輯來兜底一旦失敗,會致使比較嚴重的後果。若是系統的設計者可以認真衡量和計算這些後果的差異,就會作出更加合理的選擇。
固然還有一點就是快速失敗會把更多數量的問題暴露在用戶面前,讓用戶在心理層面有很差的影響,可是須要注意的是不暴露問題不等於解決問題,暴露問題只是讓你們看到了問題而已。爲了更好更早地暴露問題,一方面咱們須要引入更完備的測試防止問題進入生產環境,另一方面也須要引導系統使用者以更加客觀實際的心態來接受系統中的問題。
和構建穩定系統相關的另一條原則是軟件工程中常說的DRY原則,也就是:
Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.
這一條和穩定系統的關係在於,經過合理的複用設計,可以大幅度提升系統的可測性,下降調試問題的難度,提升系統的可維護性,背後的邏輯仍是比較簡單的,這裏再也不贅述。
那麼要想實踐上述原則構建可靠的系統須要注意哪些方面呢?
結合本身的工做經驗,我認爲主要是這麼幾個方面:
全部的原則都是有代價的
世界上沒有免費的午飯,借用以前Choice課程老師的一句話,堅持價值觀都是有代價的,咱們也能夠說
堅持原則都是要付出代價的。
這裏的代價包括工做量,短時間結果,解決問題的難度,帶來的項目風險等等,系統的設計者須要作出合理的權衡,付出必定的代價纔可能應用上述的原則。
刨根問底,5 whys找到根因
當問題發生時,最重要的事情在於找到問題的根因,只有咱們解決了根本的問題,系統纔會真正變得健壯起來,不然都只是假象。咱們能夠用 5 whys 的辦法來找到問題的根本緣由。
迴歸測試保障
我的認爲自動化迴歸測試至關於汽車的安全帶,咱們須要構建覆蓋度高的自動化迴歸測試保障體系,從而更早更好地發現問題,減小對於最終用戶的衝擊,把問題扼殺在萌芽狀態。
讓團隊養成好的習慣
無論是一個開發者,仍是一個開發團隊,堅持原則並非臨時起意,而須要成爲習慣。只有把原則變成習慣的我的或者團隊,纔可以真正貫徹這些原則。因此日常工做中某些看起來沒有必要的堅持原則,實際上有助於習慣的養成,而當原則成爲了團隊的習慣,這些原則才能在須要的時候得以實踐,得到回報。
不要把fail fast曲解爲快速試錯
可能有人會認爲fail fast就是快速試錯,也就是不斷嘗試,碰到正確的爲止。須要強調的是快速失敗須要很好的設計和機制保證。
以上是我對於構建可靠系統的思考與實踐總結,最近作了一次分享可是感受沒有講好因此寫下來,歡迎討論和拍磚。
本文爲雲棲社區原創內容,未經容許不得轉載。