論每行程式碼的重要性:增加了一行程式碼,讓我們提高了 3000% 的效能

語言: CN / TW / HK

本文最初發佈於 Itamar Lechowicer 部落格,經原作者授權由 InfoQ 中文站翻譯並分享

概述

我們公司運維著 15 個 Web 應用,主要的工作就是按需交付基於資料驅動的 Web 應用程式,用於支撐實時決策的制定。

這些應用的預期是在高負載下依然保持高可用。其中的主 Web 應用是一個歷史遺留的大型多服務系統。系統中的大部分服務都有超過 15 年的歷史並且經過了好幾代人的重構。試想一下,負責編寫系統程式碼的人現在可能已經離職或已經調整到其他崗位了。

過去幾年我們團隊的主要目標是就是針對這些服務進行效能優化。本次我將和你分享在效能優化的過程中,我們的一些主要經驗總結和當時決定這麼做的原因。

認知改變時刻

在某次事件中,使用者增加了對我們應用的使用率,導致我們應用的資料流量大幅增加。在此事件過程中,使用者抱怨我們的應用效能實在太差,以至於無法在應用上完成全套的業務流程。為此,我們開始利用監控工具分析應用的效能瓶頸。通過應用監控工具,我們發現服務在獲取 DB 連線上消耗了 90%的響應時間。

但是 DB 看上去一切正常,所以,我們開始分析應用的 DB 連線池。分析發現,所有的 pod 將連線池中全部可用的連線都使用了。因此我們猜測服務在關閉連線上可能有問題。於是,我們花了幾個小時時間檢查程式碼,嘗試找到連線沒有被釋放的地方。最終,我們的一個 TeamLeader 發現,pod 的存活探針在做一次簡單的 DB 心跳請求之後沒有釋放 DB 連線。隨後,我們立即在 pod 存活探針的請求中增加了一行用於釋放 DB 連線的程式碼。影響是可怕的。眨眼間,應用的效能就開始穩定下來並且使用者也恢復了正常使用。

就在此次事件的前一天,我們才執行過一次負載測試,以確保應用程式能夠承受預期的使用量增長,測試結果表明應用的效能是在正常範圍內的。然而事實證明這個測試結論是錯誤的,錯誤的測試結論誤導我們以為應用程式沒有需要修復的問題。我們深刻認識到了錯誤,我們需要做得更好。以下是我們在此次事件中學習到的一些經驗和總結。

總結一:不要使用平均等待時長作為衡量服務負載的指標——核查應用的“尾部”值

當用戶抱怨應用響應慢的時候,我們發現平均等待時長指標並沒有明顯的變化。當我們回顧了這些指標資料的時候,注意到了一些有趣的事情:之前我們是將平均請求時間作為服務等待的主要指標,因此,這次我們將 90%請求等待時長的資料做了一個圖表,看看這個圖表能不能反饋些資訊。果不其然,在使用者抱怨應用慢的時候,我們觀察到圖表中等待時長急劇增加。平均等待時長指標之所以沒有明顯變化,是因為太多的快速請求將平均值拉下來了。所以我的建議是,不使用平均等待時長,而使用 50%,90%,95%,99%的平均等待時長作為服務響應的指標。核查那些遠遠超過正常值範圍的“尾部”值是非常重要的。

總結二:在效能優化上投入時間、工具和人力

要保持應用的高效能,我們必須具備以下條件:

  1. 負載測試和負載場景——具備可用的負載測試和負載場景非常重要。

  2. 應用監控工具(APM)——諸如 Dyanatrace,AppDynamics 和 Epsagon 等工具。APM 在監控服務上可以幫我們節約大量的時間。因此在生產環境安裝至少一個 APM 是非常有必要的。

  3. 有效的日誌——有效的日誌是生產服務中斷調查和效能問題調查的基本條件。因此你必須確保應用的日誌是清晰且有用的。

  4. 日誌分析工具——你不能從很多檔案中讀取和搜尋日誌,尤其當你的服務是叢集的時候,通過檔案讀取日誌將變得更加困難。因此,花時間投產一個諸如 ELK,Grafana 或 Splunk 的日誌收集器和分析工具是非常有必要的。

  5. 專業的人力支撐——對於上面提到的知識或者工具,如果你的團隊沒有相關的專業人才,那麼你將什麼也幹不了。

因此,針對複雜的系統,我建議投入專門的人和時間來處理。(例如,SRE 團隊就能很好的勝任此項工作)

總結三:老系統將會消亡(除非我們啟用它們)

作為人類,我們都有創造新事物的衝動和慾望,並且對創造出來的產品有一種所有權感。在軟體的世界裡,在我們需要處理的矛盾中,有時候也會包含這樣的矛盾。一方面,有一個老系統需要我們維護;而另一方面,有一個炫酷的新系統我們想要去開發。那麼這個時候,我們就需要決定將時間投入到那塊。當我們面對這樣的矛盾時,我們必須記住,如果我們不繼續在老系統上進行開發和新增新功能,那麼對老系統的瞭解會隨著時間的推移而消失。因此,當我們面對系統故障或客戶新需求時,由於缺少對老系統的瞭解或者能力問題,將無法達成目標。換句話說,當我們失去對於老系統的瞭解之後,系統的 MTTR(平均修復時間)上升了。

因此,我的建議是,要時常剋制想要創造一個新的、炫酷事物的衝動,將時間投入到對老維護系統的熟悉和提升解決問題的能力上。另外,保持對老系統熟悉度的最佳方式就是嘗試在老系統中新增程式碼。

結論四:每一行程式碼都很重要

有時,當我們在編寫程式碼的時候,我們可能會忘記這些程式碼最終執行將在生產環境中,併為一個真實使用者的真實工作服務。上面提到的我們親身經歷的案例中,僅僅只是因為程式設計師忘記了釋放 DB 連線(一行程式碼而已),就可以干擾一個使用者的正常工作(那些工作受影響的使用者估計很不願意給我們付錢)。

我的建議是:

想象一下(雖然很難),在世界的另一端,某個使用者的工作完全依賴你編寫的程式碼,同時試想一下,你寫的每一行程式碼都將影響其使用應用的體驗。

在 CI 或者 CD 環節執行負載測試。如果你想確保程式碼高可用,那麼就針對每個即將投產的 PR 或版本都進行負載測試。

當你發現效能問題的時候,請懷疑每一行程式碼——據我們的經驗,程式碼中的每個字元都有可能是導致效能的瓶頸。

總結

此文章闡述了我們在系統性能優化上的全部經驗教訓和體會心得,我希望通過此文章能夠幫助你意識到系統性能缺陷所存在的潛在風險。

我認為,應用的效能應該被視為最高優先處理事項。因為和終端使用者不能使用系統相比,漂亮的 UI 和炫酷的產品都顯得微不足道。

我寫的這些結論都是我根據日常效能優化的經驗總結而來,因此,在我看來,上面的所有結論都是每一次成功的效能優化的基石。所以,我也希望你能發現它們的用處。

原文連結: