從網路連接讀取時程序無限期掛起
對以下內容的更新:
我在不同數據中心的 Debian 虛擬機上的不相關腳本上遇到了類似的問題。
這看起來很像這裡描述的問題(和問這個問題的人一樣,我沒有在伺服器前面配置代理)。
下面描述的主要區別在於,當我附加到掛起的程序時,我看到一個呼叫
recvfrom
而不是read
:$ strace -p 17527 Process 17527 attached - interrupt to quit recvfrom(3,
然而,Python 並沒有任何被代理的印象:
>>> import os; print os.getenv("HTTP_PROXY"), os.getenv("http_proxy") None, None
所以我還是很困惑。可悲的是,連結的問題也沒有最終答案。
(我也想知道這個問題是否相關,但 S3 似乎不太可能無法遵守
Connection: close
標頭。)我有幾個 Debian (Wheezy, x86_64) 伺服器都表現出以下行為:
所有伺服器都有一組 cron 作業,其中包括從 S3 提取數據。這些通常執行良好,但偶爾會
ps aux
顯示幾小時或幾天前開始的一些作業仍在執行,並且沒有乾淨地完成。用顯示檢查它們
strace -p <pid>
,在所有情況下,該過程都掛在讀取命令上。例如,我剛才檢查的一個程序的輸出是:$ strace -p 12089 Process 12089 attached - interrupt to quit read(5,
檢查打開的文件描述符給了我這個:
$ sudo lsof -i | grep 12089 python 12089 user 5u IPv4 809917771 0t0 TCP my.server.net:35427->185-201.amazon.com:https (ESTABLISHED)
起初我認為這只是由於沒有在 Python 腳本中設置讀取超時,但事實並非如此,原因如下:
- 當使用相同的程式碼在我們的 OS X 機器(所有 10.5,i386)上執行相同的作業時,不會發生這種情況。
- 設置超時的腳本變體(60 秒,使用-這
socket.setdefaulttimeout
是在 Python 2.7 中,但程式碼庫必須與 2.5 兼容)從昨天開始被掛起。- 另一個不是 Python 的程序似乎偶爾會表現出類似的行為。在這種情況下,一個 Python 腳本正在執行一個
svn up --non-interactive
程序(使用subprocess.Popen
, 是值得的)。SVN 程序的情況類似——
Python 正在等待 SVN:
$ strace -p 28034 Process 28034 attached - interrupt to quit wait4(28127,
SVN 正在等待
read
呼叫完成:$ strace -p 28127 Process 28127 attached - interrupt to quit read(6,
該讀取指向另一個外部主機:
$ sudo lsof -i | grep 28127 svn 28127 user 3u IPv4 701186417 0t0 TCP my.server.net:49299->sparrow.telecommunity.com:svn (ESTABLISHED) svn 28127 user 6u IPv4 701186439 0t0 TCP my.server.net:49309->sparrow.telecommunity.com:svn (ESTABLISHED)
(似乎正在更新的目錄上
svn:externals
設置了一個屬性ez_setup svn://svn.eby-sarna.com/svnroot/ez_setup
;根據他們的網站,我認為這是重定向到 telemunity.com)其他可能相關的點:
- Mac 上的 Python 環境是 2.5。在 Debian 機器上,它是 2.7。
- 我對 SVN 並不精通,我不知道它掛起的原因是否基本相同。我也不完全確定它的含義
svn:externals
是什麼;這是在我之前建立的。- Python 腳本本身正在從 Amazon S3 檢索大型(在某些情況下約為 10MB)數據塊,這往往很慢(我看到下載時間長達三分鐘,與伺服器——即使在不同的數據中心——需要很長時間才能相互通信)。同樣,我們的一些 SVN 儲存庫也相當大。所以這基本上是說其中一些操作無論如何都是長期執行的,但在某些情況下它們似乎也會掛起數小時或數天。
- 今天早上,OOM 殺手在一台伺服器上乾掉了 MySQL。仔細觀察,記憶體使用率為 90%,交換使用率為 100%(如 Monit 報告的那樣);殺死大量積壓的 Python 作業將這些統計數據分別降低到 60% 和 40%。這給我的印像是,至少有一些(如果不是全部)數據正在被下載/讀取(並在程序掛起時保存在記憶體中)。
- 這些 cron 作業從 S3 請求資源列表,並相應地更新 MySQL 表列表。每個作業都以相同的列表開始,因此將嘗試請求相同的資源並更新相同的表。
- 我能夠從一個掛起的程序中擷取一些流量;這對我來說有點不可思議,但我想知道它是否表明連接處於活動狀態並且正在工作,只是非常非常慢?我提供了它作為一個要點,以避免混亂(我應該注意這是大約兩個小時的擷取時間):https : //gist.github.com/petronius/286484766ad8de4fe20b 我認為這是一個紅鯡魚。該埠上有活動,但它與 S3 的連接不同——它只是其他隨機伺服器活動。
- 我試圖在另一個數據中心的一個盒子上重新創建這個問題(一個執行相同版本的 Debian 並具有相同系統設置的虛擬機)但沒有運氣(我認為這個問題可能與這個問題有關,但是遇到這些問題的盒子不是虛擬機,並且根據 ) 沒有丟包
ifconfig
。我想這表明存在網路配置問題,但我不確定從哪裡開始。所以我想我的問題是:
- 我可以在系統級別解決這個問題,還是每個單獨的程序都有問題?
read
OS X 和 Linux 如何處理我需要知道以避免無限掛起程序的呼叫有什麼根本不同嗎?
我可以在系統級別解決這個問題,還是每個單獨的程序都有問題?
很難說,因為它不知道協議級別發生了什麼。基本上,
read(2)
將無限期阻止提供:-
- TCP 連接保持打開狀態。
- 您預計至少有 1 個字節的數據到達。
- 發件人尚未準備好向您發送數據。
現在,可能是該過程出現了問題,例如另一端在發送更多數據之前先期待您的響應,或者另一端的先前響應期望 SVN在請求更多數據之前執行*其他操作。*例如,假設返回了一個錯誤響應,它應該迫使客戶端重新發送一些資訊。
你不能優雅地解決這個問題,因為它不可能從你所擁有的資訊中確定這個數據的發送者期望你做什麼。但是,有幾種可能的方法可以避免並報告該問題。
wait
與其在簡單的阻塞模式下使用,不如wait
在父程序中執行和配置警報。現在,當該過程在固定時間內未能完成時,您可以將其殺死並報告發生了這種情況。一種廉價的方法是更改 subprocess.Popen 以呼叫該timeout
命令。- 修改讀取,使其設置讀取超時套接字選項。您可以通過更改程式碼來做到這一點,或者 - 使用插入器來覆蓋預設
socket
系統呼叫,以向接收器添加超時。這兩者都不是微不足道的。這可能會導致svn
行為異常。OS X 和 Linux 如何處理我需要知道以避免無限掛起程序的讀取呼叫有什麼根本不同嗎?
我不知道這個問題的答案,但是如果兩者的行為都是正確的,那麼它們的行為方式應該相同。如果您嘗試從尚未準備好向您發送數據的套接字讀取,則無限期地阻塞流是預期的行為。
總的來說,我認為你最好的攻擊選擇是期望你的
svn
命令在特定的時間段內完成。如果它沒有殺死它並報告你這樣做了。