1. 程式人生 > 其它 >避免線上故障一些建議

避免線上故障一些建議

1、謹慎大切面

引發問題示例:

之前有次故障,因為一個大切面裡包含了一個無界佇列,直接out of memory了

  1. 自定義切面時,要注意切面所佔用的資源和耗時。如果切面包含外部呼叫等操作,前後一定要打日誌。
  2. 業務邏輯程式碼不建議catch(Throwable),因為針對系統的Error,人工處理的原則很難把控,但是切面是例外。切面建議catch(Throwable),不要因為切面問題影響正常的業務邏輯。

2、合理的超時和重試

引發問題示例:

上面提到的故障,它的上游服務看到下游服務的現象就是超時。超時會造成執行緒池長時間不釋放。結果上游服務執行緒池被打滿。級聯故障,整個服務鏈路雪崩。

  1. 超時和重試實踐設定好需要壓測,如果下游全部超時故障,也要做到自身不能掛。

3、事務裡不能包含外部呼叫和大查詢

有個事故是在事務包含了一個傳送MQ的操作,當時的技術選型選用的rabbitMQ,積壓敏感。MQ同步阻塞寫入,積壓時寫入不了,造成事務無法提交。DB行鎖升級為表鎖。整個DB掛了。

  1. 在Java裡,事務也一般用切面來控制,建議使用註解,而不是直接配置掃描包或者類的方式,避免操作被忽略。
  2. 事務中的查詢建議使用id或者唯一索引作為查詢條件。最好的方法是不用事務,比如可以用CAS原理先查詢一般符合預期再進行更新這樣的輕量級操作,這樣替代事務操作不成功則回滾的重量級操作。

4、程式碼即註釋

現在講究程式碼即註釋。單純的註釋會因為疏於維護,不能表達程式碼真正的含義,反而造成困擾。但是可以把真正要表達的意思寫到日誌裡。

擔心日誌太多影響效能,可以使用debug級別日誌,線上實際列印是info級別日誌的話,編譯時日誌就被過濾掉了,並不影響效能

5、不要做計劃外的優化和重構(避免隨手優化)

之前有個故障是一個程式碼潔癖的開發人員看到一個都表達賬戶的欄位,一個用了accNO,另外一個用了accontNO。於是他對程式碼進行了重構。

程式碼分層也會把物件分成DO、DTO、BO。這些物件有時候會用BeanUtils.copy來進行轉換。結果這次重構沒有改全,物件拷貝的時候一個值沒有拷貝過來,導致線上所有建立賬戶操作都失敗。

6、及時處理廢棄邏輯和臨時程式碼(設計包含擴充套件,開發只留有用)

一些同事喜歡把現在用不到,但是將來有可能用的程式碼留下來。我的建議是涉及時把怎麼擴充套件都想好是對的,但實際程式碼裡只留用到的,其他可以真正要的時候再寫。

針對一些拷貝過來可能會用到的程式碼可以進行一次提交再刪除,這樣就可以藉助git版本管理等工具在需要的時候方便的找回來。前提是提交的註釋一定要寫好。

因為隨著人員更替,線上一直在用的程式碼還好,改動起來心裡反而有底氣。找不到誰在用的程式碼,改起來心裡更加忐忑。

7、不要吝嗇回滾

好不容易一個服務上線了,聽說整個鏈路裡發生了故障,可能聽起來和自己這邊關係不大。絕大多數情況可能真的沒有關係。但是我覺得一個比較好的實踐是聽到出現了故障,那咱就先回滾吧。起碼讓人家排查故障的時候也安心,肯定和咱們這邊的釋出沒有關係。

之前有個故障是服務週二上線後當時沒有什麼問題,到了週四在高峰時段爆發了。開發人員只回滾了前一天晚上的服務。回滾後故障沒有恢復,後來領導下令將所有服務版本回滾到了週一,包含了那個服務,才恢復了現場。

8、不要掩蓋錯誤

有時候程式碼有個邏輯錯誤,為了避免挨一頓小罵,自己偷偷修改上線。其他人不知道。而修改的邏輯沒有經過設計評審、程式碼走查。從而引起更大的問題,出了故障了大家還不知道這塊有修改,現場恢復也慢。從而造成大故障。

9、元件等引數修改需要配合壓測、避免想當然

有個故障是因為修改了MQ引數引起的,開發人員修改了消費端連線MQ的引數,這個引數調整實際上是把BIO阻塞方式進行消費訊息改成了非阻塞。以為這個引數的調整可以提高效能,沒有壓測,造成了大故障

10、原理要理解透徹

上面的故障中為什麼把BIO阻塞方式進行消費訊息改成了非阻塞反而在高峰期高併發場景下造成故障呢?下面是個極簡版的原理圖: