異常處理的最佳實踐(上)

在開發一個系統的時候,我們往往會將大部分注意力集中在如何實現具體的功能上,而對於異常和錯誤處理則通常不受重視——感覺這事情無從下手,又看不到什麼政績,簡直吃力不討好。而異常處理本身,也常常被作爲語言特性的一部分輕描淡寫地一筆帶過,很少有系統性地對其正確的使用方式進行介紹。這簡直是暴殄天物!使用異常來處理錯誤明明是一項偉大的發明,是將程序員從條件判斷的地獄中拯救出來的神器!本文將給大家介紹一下異常處理的一些最佳實踐。

異常的錯誤處理方式

很多程序猿害怕異常,他們覺得異常是萬惡之源,會弄停他們的軟件——太陽當空照,花兒對我笑,程序歡快地跑着,突然來了一個莫名其妙的錯誤輸入,就崩潰了——這是多麼令人討厭的事情啊!於是他們對異常處理的理解往往僅限於把異常catch住,不要去打擾系統的正常運行,就像這樣:

稍有研究的程序猿甚至懂得使用例如一個全局的UncaughtExceptionHandler來一勞永逸地”解決”這個麻煩。

然而這種行爲的本質是隱藏錯誤——早在上個世紀,偉大的VisualBasic曾發明了個叫On Error Resume Next的神奇咒語來滿足類似的需求,其結果是臭名昭著的。對錯誤的發生採取鴕鳥對策,往往會造成無法預料的結果,令程序運行到無法預知的狀態。一旦遇到真正需要解決的問題,即使消耗大量的人力物力也難以定位原因,甚至直接導致項目的失敗。

另一種常見的異常處理方式是使用返回碼——catch到異常後,便返回一個錯誤代碼。這也是異常恐懼症的一種典型症狀:覺得異常是可怕的殺手,把他轉成溫和的返回碼,就變得無害了。可惜返回碼並不是什麼好東西,其意味着一個需要不斷維護的龐大列表(然而這事太麻煩了實際上並不維護)。程序猿需要在正常邏輯中追加各種條件判斷來處理無窮無盡的特殊情況(然而錯誤碼往往被簡單忽略實際上並不判斷)。正確地使用返回碼極其麻煩,而一旦返回碼沒有被正確地使用,它就會化身爲和忽略錯誤一樣麻煩的害獸,吞噬着軟件的質量。

異常處理的正確策略

異常被髮明出來絕對不是爲了僅僅弄停你的程序。大家遇到的crash,往往是因爲未捕獲的異常。未捕獲的異常,意味着系統不知道如何處理這個異常,於是最妥當的方式就是止損,不再繼續運行以免發生不可預料的操作。那麼我們應該在何時捕獲異常呢?只有當我們知道發生問題時,應該如何恢復到一個安全的狀態的時候。有了一個安全的狀態,我們就可以保證之前的錯誤不影響後續的數據處理。

簡單全局策略

異常最基本的價值在於可以攜帶大量的信息,尤其是錯誤消息和StackTrace,對於定位問題具有很大的幫助。因此一種簡單的異常處理策略是:在開發階段不處理異常,任由crash發生,並通過其中的異常信息來定位問題。在正式發佈以後,加入一個簡單的全局異常處理單元,其功能是記錄下異常信息並使用合適的方式上報。許多客戶端軟件都採取了類似的策略,賭的是通過測試來儘可能暴露問題,並利用異常便於定位問題的特性大幅提高bug修復的速度。只要發佈前做了充分的測試(呵呵),真正會發生crash的場景很少。

針對服務端請求的處理策略

服務端程序猿最害怕crash,一旦停止服務,負責人往往會被攪了清夢,結果一查通常只是個非法輸入,採取的措施也不過是重啓,因此他們會有很強的動機寫一個什麼都不幹的全局異常處理函數(最多打打log)。

隱藏錯誤的危害這裏就不多說了,上述處理方式往往還會造成客戶端無法及時得到響應只能坐等超時。有些Web框架帶有默認的異常處理策略,雖然不會crash,但常常導致異常信息泄露給調用者,造成不必要的安全隱患。

所幸對於服務端來說,請求的處理往往是相互獨立的,於是只要拋棄那個出現錯誤的請求,即可回到安全狀態。因此對於服務端程序來說,通常理想的異常處理策略是針對每一個請求的入口進行try-catch,將異常catch住後封裝成對調用者友好,並且不會泄露系統信息的錯誤消息,返回給調用者。於此同時,將詳細的錯誤消息及請求內容記入日誌。後續進行troubleshooting時,可以根據時間戳、錯誤碼、請求id等查詢到日誌中的相應記錄。

針對用戶操作的處理策略

對於一個需要長期穩定工作的客戶端軟件來說,簡單的全局異常處理策略往往是不夠的。每個Crash的發生,都會影響用戶的體驗和程序猿的業績,即使再充分的測試,也難以避免漏網之魚,尤其是環境因素造成的詭異問題。

對於客戶端軟件來說,業務邏輯的入口往往是用戶的操作。每當舊操作完成之後,新操作發生之前,往往意味着獨立的業務邏輯暫告一段落,可以視爲安全狀態。因此一個較爲妥當的策略是在每個用戶操作的處理函數中,均加入try-catch。異常catch住時,顯示一個對用戶友好的報錯信息,同時將異常信息以及用戶操作的相關數據記錄並上報。

注:如果用戶操作觸發了一個進度條之類提示進展的界面,需要在異常處理邏輯中對UI進行重置。

小結

前文所述的策略主要側重在最上層的異常處理方式,原則上所有從底層拋上來的異常均會在這裏做最後的處理。繞過這層,便會成爲未捕獲的異常。通常情況下,底層即使捕獲了異常也做不了什麼,而這裏纔是真正能夠對錯誤的發生採取措施的地方。

因此正確的異常處理方式實際上是非常簡單直接的:什麼都別做,交給最上層處理;最上層也只需報錯,記錄,然後恢復到安全狀態繼續執行後續邏輯。

未完待續

本文介紹了大體的異常處理策略,然而還有一些細節可以幫助我們更好地在異常發生時定位問題。欲知後事如何,且聽下回分解。

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