漫畫:如何分析運行中的 Python 程式?

  • 2020 年 3 月 13 日
  • 筆記

內容簡介

  • 如何使用 py-spy
  • 如何讀懂火焰圖

遇到的問題

大佬組長透露出幾個關鍵資訊:

  • 1. 要排查的是線上正在運行的 Python 程式
  • 2.「凌晨 3 點多的時候可能出現」,表示問題並不是每天都出現的
  • 3. 問題現象是程式卡死,重啟後,有可以正常運行

線上服運行在真實環境,使用真實數據長時間運行,這種非必發性的錯誤通常難以在測試服或灰度服中發現,而且這種錯誤看日誌通常難以判斷出現這種問題的真正原因,可能其他地方的程式碼出現了問題,但沒有被處理,導致異常狀態一直堆積,一段時間後才出現的問題。

根據上面的關鍵資訊,我開始進行如下思考:

  • 1. 思考是否可以構建「最小實例進行復現呢?」,可以復現就說明找到了問題。
  • 2. 是程式碼的問題還是其他依賴服務存在問題導致本 Python 程式出現問題?

自己在測試環境運行相同的程式碼,發現沒有任何問題,那是其他依賴的問題?這麼多依賴服務,一個個查是不現實的,那隻能在程式碼中加多點日誌,然後再放到線上去看了?

其實還有使用更簡單的方法去定位錯誤,那就使用 py-spy

使用 py-spy

py-spy 是一款開源的 Python 程式的抽樣分析工具,使用 py-spy 可以很直觀的查看 Python 程式的進程堆棧以及不同方法的耗時等,整個監控方式不需要對正在運行的程式做任何處理,即你不要修改程式程式碼也不需要重啟程式。

py-spy 使用 Rust 語言開發,Rust 語言是一門號稱 C/C++ 的運行速度、類 JavaScript 寫法的語言,簡單而言,就是開發效率高運行效率也高。py-spy 基於 Rust 語言提供的強大特性,使得它可以安全的用於生產環境中的 python 程式。

通過 pip 安裝 py-spy

pip3 install -i https://pypi.doubanio.com/simple/ py-spy

安裝完後就可以使用了,如果你是 windows 用戶,需要下載預構建的二進位文件進行安裝,細究可參考官方說明(在最後參考一節)。

通過 py-spy help 查看該工具支援的指令,總共就 3 種不同的指令,非常簡單。

首先來使用 top 命令,它會生成類似 Unix 系統中 top 命令的效果,它有兩種使用方式,分別如下。

# 如果python程式正在運行,直接通過pid進行取樣  sudo py-spy top --pid 12345  # OR  # 如果python程式沒有運行,可以使用如下命令  sudo py-spy top -- python myprogram.py

因為線上 Python 服務本身就是運行狀態,所以直接通過 pid 去查看運行進程中的資訊,這些資訊會依據 py-spy 的取樣實時更新。

從上圖可以看出,top 命令會顯示出當前 Python 進程中 GIL 鎖的使用率、活躍執行緒率、執行緒數等總體資訊,此外還會列出程式中不同方法的佔用時間,其中的資訊包含了使用的第三方庫所佔用的時間。

此外,還可以讓 py-spy 生成火焰圖(flame graph),通過火焰圖可以更直觀的判斷出程式的性能情況。

py-spy 生成火焰圖只需使用 record 命令則可,與 top 命令類似,同樣有兩種使用方式,如下。

py-spy record -o profile.svg --pid 12345  # OR  py-spy record -o profile.svg -- python myprogram.py

-o 參數用於指定火焰圖生成的路徑。

如果你不清楚怎麼看火焰圖,不用緊張,後面會介紹火焰圖的使用方法。

如果你的程式突然卡死了,你又不清楚它為何會卡死,此時就可以通過 py-spy 的 dump 命令來查看當前程式的調用堆棧,通過調用堆棧來判斷 Python 程式掛在何處。

dump 命令只能用於正在運行的 Python 程式。

py-spy dump --pid 12345

從圖中可以很清晰的看出當前 Python 程式中所有活躍執行緒的調用棧。

加多 –locals 參數可以將每個堆棧幀關聯的局部變數也列印出來,如下圖。

py-spy 的簡單用法就介紹完了。

讀懂火焰圖

火焰圖通常是 svg 圖片,可以直接通過 Chrome 瀏覽器打開。

py-spy 生成的火焰圖其火焰是向下的,而有些工具生成的火焰圖其火焰是向上,樣式不同,但沒有什麼本質區別。

看到剛剛 py-spy 生成的火焰圖。

火焰圖的 y 軸表示程式的調用棧,每一層都是一個函數,調用棧越深,火焰就越高,最底部就是當前正在執行的函數,上方都是它的父函數。

火焰圖 x 軸表示抽樣數,如果一個函數在 x 軸佔據的寬度越寬,表示它被抽樣程式抽到的次數越多,也就表示該方法的執行時間較長。

需要注意,火焰圖 x 軸不代表時間,而是所有調用棧合併後,按字母順序排列而成。

此外,火焰圖的顏色沒有特殊含義。

火焰圖是可以互動的。

  • 1. 滑鼠懸浮

當滑鼠懸浮在火焰的某一層,火焰圖都會顯示出當前層對應的完整函數名、抽樣抽中的次數、佔據總抽樣次數的百分比。

  • 2. 可點擊放大查看

可以點擊火焰的某一層,火焰圖會水平放大,該層會佔滿 x 軸,從而顯示出該層的詳細資訊。

  • 3. 可以搜索

點擊火焰圖中的 Search 會顯示一個搜索框,用戶可以輸入關鍵字或正則表達式,所有符合條件的函數名都會高亮顯示。

其實 Chromme 瀏覽器本身就可以生成訪問某網站時的性能火焰圖。

打開開發者工具 -> 切換到「Performance」 -> 點擊「錄製」按鈕開始記錄數據,此時訪問 github.com,等網頁完全載入後,停止錄製,此時,開發者工具就會顯示出一個時間軸,而它的下方就是一個火焰圖。通過這個火焰圖可以詳細的分析當前頁面的性能。

與傳統的火焰圖不同,x 軸是時間軸,而不是抽樣次數。

參考

1.py-spy github:https://github.com/benfred/py-spy

2. 如何讀懂火焰圖?:https://www.ruanyifeng.com/blog/2017/09/flame-graph.html