1. 程式人生 > >看夕陽西下,看流星過往,看星海沉浮,看人間滄桑

看夕陽西下,看流星過往,看星海沉浮,看人間滄桑

廢話系統安全

最近一年,飛機失事的事情發生了好多起。於是乎,飛機是否是一種安全的出行工具的討論又再一次燃起了戰火。

我無意對該話題展開討論,只是可以舉個例子說明一下。

如果A城市一年出了10次搶劫事件,B城市一年出了1次殺人事件。那麼可能會有很多人覺得B城市相對更不安全。

正是因為飛機失事一般會帶來非常嚴重的後果,所以飛機的設計比其他的交通工具要優秀的多。

這裡,我們參考一下傳統工程的設計思想,來看看我們的系統是否安全。

先說明一下,在本文中,安全的定義比較廣義,即是系統不會被災難性的破壞。

比如:汽車自動駕駛系統不會突然指令錯誤,賬務系統不會出現計算錯誤而對使用者造成資金損失。

所以可以認為是一種健壯性。

從案例看起

那麼,首先看一個案例——法航447號航班空難

簡單介紹一下官方公佈的失事原因

  1. 飛機在夜晚啟用自動駕駛模式後,進入亂流區域,然後皮托管結冰。皮托管是安置在飛機外面,根據氣流經過的速度而取得飛機速度的一種裝置。所以皮托管結冰後,飛機無法得知當前速度,系統解除了自動駕駛模式並開始報警。
  2. 駕駛員接手開始手動駕駛飛機。而突然聽到報警有些緊張的駕駛員由於無法得知當前速度,所以拉昇飛機。(人們只是猜測他可能是出於本能)
  3. 擡升角過高,所以導致飛機失速。想象一下,飛機如果水平向前,將獲得向前的動力,如果機頭過高,則水平方向的速度將會降低,最終失速而往下掉。
  4. 最終掉入大海。


皮托管,右邊進氣左邊出氣,根據氣流測速

這個案例其實給我們一個非常深刻的教訓,除了飛行員駕駛失誤意外,系統設計不足也佔有非常重要的原因。

1、合理的 Barrier

在設計中,Barrier是一個非常重要的考慮因素。Barrier是防止系統出錯,或者說是當系統出現異常的時候的一種自我防禦機制。

例如:

家庭用電時要考慮空氣開關。

自動扶梯要考慮緊急制動。而不是設定一個警示牌教導人遇到情況怎麼從扶梯上跳下來。

房間要考慮煙霧報警器並連線消防局。而不是安排一個人敲鑼高呼天乾物燥小心火燭。

汽車要安裝安全帶。

而本案例中,皮托管會結冰,並且結冰後無法測速這個難題在設計的時候確實有過考慮,而它的防禦機制則是:系統報警。

恰巧,接手處理報警的飛行員經驗不足,而最終導致失事。

當我們在設計系統的時候,需要考慮怎麼設定一個良好的Barrier,盡最大可能去解決問題,而儘可能少的將問題拋給愚蠢的人類去處理。

2、必要的 Barrier

對於關鍵的元件,必要的 Barrier 則是為它建立一個基於完全不同實現的備用元件。假如元件A失效的可能性為1%,而備用元件B失效的可能性為2%,那麼由元件A元件B組成的新元件失效的可能性就只有1%*2%為0.02%了。大大降低了故障可能發生的機率。

例如:

  • 家裡的空氣開關往往是多層控制,以避免單個失效(分閘不跳總閘跳)。
  • 民航飛機往往不止一個發動機引擎,起落架不止一種控制方式,通訊方式不止一種。。。
  • 汽車在腳剎失靈的情況下,可以使用手剎。
  • 醫院急救室會配置一個柴油發電機。


如圖所示,如果Barrier是基於相同的演算法或者實現方式,那麼它們可能會有同樣的漏洞,無法起到真正的防禦作用。

法航失事的案列正好也說明了空客A330-203的測速裝置設計不足。如果再配有另外一種不基於氣流測速的裝置,可能不會導致無法測速,也不會最終導致系統報警。 

所以當我們在設計關鍵的模組或者演算法的時候,需要增加一種或者多種不同實現,進行交叉校驗。以保證模組或者演算法輸入輸出正確。


如圖,模組1與模組2的結果核對相同後,才確定最終輸出

3、高內聚並低耦合

曾經看到過一個案例,有位先生開著汽車出去兜風,發動機突然熄火。結果剎車和方向盤同時失靈,因為發動機熄火後沒有助力提供了。

所以,我們的功能必須首先高內聚,形成一個模組一個元件,這樣才能對它新增Barrier。 而模組間的聯絡越少,則它的Barrier更容易實現且更堅固。

前面那個剎車失靈的例子也正說明了發動機、剎車和方向盤這些部件之間的關係過於緊密而導致互相影響,一個失效而帶來了連鎖反應。

從程式的角度來看,可以舉個例子,如果一個VO從前端經過各個功能模組最終傳遞到資料庫,那這樣無法避免模組間的互相干擾——假如變更了VO中的一個變數,則可能模組A、B、C可能同時受到影響。同時,為模組A、B、C設定的Barrier可能也無法正常工作。從測試的角度來看,這也是一種災難。

總結:

反思我們的系統,是不是有這樣的情況:

  • 沒有必要的校驗或者不合理的對異常情況處理方式而出錯。
  • 設計時過多考慮人工介入處理而不是災難恢復。
  • 關鍵部分沒有交叉校驗而採用單一方法而出錯,往往表現為單一方法測試不充分。
  • 改了模組A,模組B執行出狀況了。
  • 看起來一系列巧合的事件同時發生。(其實是相互影響造成的,而恰恰是需要各自獨立的地方)
  • 他的接口出異常了,我的系統崩潰了。

一個好的軟體系統,需要程式碼的功能內聚,職責單一,模組間低耦合,並且需要每個重要的功能設定合理和必要的Barrier以保證輸入輸出正確。

因為彼此不信任,所以安全。

引用列表: