1. 程式人生 > >遺留問題,排雷會炸,不排也會炸!

遺留問題,排雷會炸,不排也會炸!

本週一個故障,記錄一下。

諸位知道,歷史遺留程式碼,只要能執行,一般都不願意去動的。 然而, 遺留問題就像雷區,排雷會炸,不排也會炸。 這不,本週一個電子卡券匯出的雷,炸得我真是“面目全非”。

具體程式碼不必細講。 看官只需懂得:電子卡券匯出是一個PHP後臺任務,接收訊息佇列的匯出訊息來處理任務。 在捕獲了一個異常之後,走到了異常處理分支。異常分支有句程式碼 logger->warn 。 這有何錯 ? logger 並沒有 warn 方法 ! IDE 也提示的。 於是 PHP 報 fatal error ,直接退出了程序。 這樣,匯出任務程序無法啟動,無法處理訊息,佇列阻塞,前端就一直顯示報表正在生成中。

為什麼 logger 沒有 warn 方法 , 卻還明顯地寫著 logger->warn ,甚至歷經兩年都沒有發現 ? 想必部分讀者已經瞭然:因為寫在異常捕獲中。異常測試,是很容易被忽略的分支。

值得提及的是,logger 有個 warning 的方法。 然而,開發者習慣用 warn 而不是 warning ,並且 warn 是動詞,warning 是名詞。 打日誌本應該用 warn 而非 warning 。 底層 API 設計者希望用新的方式取代老的方式,也必須考慮到大多數開發者的慣用法。

另一個問題是: 電子卡券匯出為什麼處理這麼慢?

進一步閱讀程式碼可知,電子卡券匯出程式碼,裡層竟然對每一個訂單迴圈進行處理:訪問 DB 獲取卡券資訊,呼叫使用者 API 介面,呼叫重量級的訂單詳情介面。環環相扣,真是令人“拍案叫絕”啊!

為什麼會出現這種情況 ? 原來,為了方便,匯出程式碼複用了列表程式碼。而列表程式碼只需要處理 20 個訂單, 但匯出程式碼卻會處理數萬訂單。可想其效能可以低到地心裡面去。推測, 可能當時修改列表程式碼的時候,並沒有注意到匯出複用了列表程式碼,只是在列表程式碼裡修改了,但同時匯出的處理也影響到了,而修改者渾然不覺。

呼叫重量級的訂單詳情介面,也值得商榷。 列表與匯出要顯示的資訊不過七八個,卻要呼叫訂單詳情介面,獲取近百個欄位。真是為了一個輪胎,拖了一輛卡車。須知,這事並不鮮見。注意到,匯出外層批量呼叫了一次新的批量詳情介面,卻不料裡層為了訂單列表又迴圈對每個訂單呼叫了老的訂單詳情介面,令人以淚洗面!

這裡有三個啟示:

  • 對於大量資料,迴圈呼叫單個介面,乃開發之忌。列表如果這麼做,一定會有一天跪掉;匯出如果這麼做,要麼會拉跨被呼叫的業務方,要麼自己慢到比蝸牛還慢。
  • 心裡要有全景圖,能評估影響範圍(極為重要!),不然,很容易因為一個改動影響到另一個地方。此是開發常理。
  • 不要為了一個輪胎,拖一個卡車。不要輕率地複用一個大的業務模組。如果要複用,請通讀這個業務模組,並保證複用的模組是輕量級的。

處理一個故障,可以折騰很多時間。比如 “趴墳”啊,開會啊,每一個細小的地方,一定要分析得水落石出,令人心服口服,無言以對,—— 哪怕這坨程式碼再也不會在線上執行。 有這時間,可以多去發現和思考系統中的潛伏著的問題,根治之。

PS: 無意看到歷史上的今天,發了一篇“程式碼問題及對策” ,裡面早已談到了這些問題。 後人哀之而不鑑之,亦使後人復哀後人矣。