加速重複的 python 呼叫(或者,將復雜的正則表達式移植到 sed)
我是一名學術醫學物理學家。我做的實驗會產生大量數據,而且執行起來很昂貴。我的大學有一個備份系統,它由一個廢棄鹽礦中的機器人磁帶庫組成,它使用 IBM 的Spectrum Protect(呼叫為
dsmc
),我用於異地備份。雖然我可以發送到鹽礦的總大小沒有限制,但每天的傳輸限制為 200 GB。據我所知,沒有辦法讓 Spectrum Protect 客戶端遵守此限制,並在達到傳輸限制後停止。如果有人突破了這個限制,伺服器會鎖定節點,我必須向某人發送一封卑鄙的道歉電子郵件,要求他們解鎖它。他們告訴我使用過多的頻寬,然後在 24-48 小時後解鎖節點。
為了解決我以離散塊創建數據(在實驗日)並且在每月或每週的頻寬限制之下的事實,我編寫了一個簡單的包裝腳本來解析輸出
dsmc
並殺死如果它變得太大,則轉移。解析是通過
dsmc
使用簡單的 python 腳本將 bash 中的輸出視為此處的文件來完成的:#!/bin/bash # A silly wrapper script to halt TSM backups # # Usage: sudo /path/to/script /path/to/backup/location # # Requires python3 accessible as python3, and the regex / os modules. # Tested on MacOS and Linux BYTES_SENT=0; #MAX_SIZE_TO_SEND=150 #Bytes, for testing MAX_SIZE_TO_SEND=$[185*(2**30)] args=("$@") sudo rm -f /tmp/dsmc-script.PID function outputParser() { python3 <<'EOF' import os, re rex=re.compile(r"Normal File\-\-\>\s*?([,0-9]*,?)\s*?\/") valueToParse=os.environ.get('line'); match=rex.match(valueToParse); try: stringToReturn = str(match.group(1)); stringToReturn =stringToReturn.replace(',',''); except AttributeError: stringToReturn = ""; #Check for failed transfers failedResults = re.findall(r"\*\* Unsuccessful \*\*", valueToParse); nFailedResults = len(failedResults); if (nFailedResults >0): stringToReturn = ""; print(stringToReturn); EOF } #I am sure that the above is a one-liner in sed or awk. I just don't know what the one line is. function trapCaught() { #Do cleanup, not shown echo ", quitting." } trap trapCaught sigint killCount=0 startTime=$SECONDS while read -r line; do echo "$line" export line; X=$(export line=$line; outputParser) if [[ ! -z "$X" ]]; then BYTES_SENT=$[$BYTES_SENT + $X] echo "Sent $X bytes, $BYTES_SENT in total" fi if (( BYTES_SENT > MAX_SIZE_TO_SEND )); then if (( killCount < 1)); then echo "STOPPED BACKUP BECAUSE $BYTES_SENT is GREATER THAN THE PERMITTED MAXIMUM OF $MAX_SIZE_TO_SEND"; killStartTime=$(( SECONDS - startTime )) pid=$(cat /tmp/dsmc-script.PID) echo "PID is $pid" echo $pid | sudo xargs kill fi killCount=$[$killCount + 1]; timeKillNow=$(( SECONDS - killStartTime )) rm -f /tmp/dsmc-script.PID if (( killCount > 100 || timeKillNow > 30 )); then echo "Taking too long to die; retrying" echo $pid | sudo xargs kill -9; sleep 0.1; sudo kill -9 0; fi fi done < <( sudo dsmc incr ${args[0]} & echo $! > /tmp/dsmc-script.PID )
這有效,適合我的目的。然而,性能差得近乎糟糕,我認為這是因為
while
循環中的每次迭代都會產生另一個 python 解釋器/腳本組合的實例。鑑於我無法更改限製或二進制編譯 blob 的行為
dsmc
,我有三個相關的問題:(a)這是解決此問題的明智方法,還是我缺少一種更簡單的方法,例如高級巫毒教
netstat
?(b) 鑑於 python在循環中的每次迭代中實際上所做的基本上是完全相同的,有沒有辦法記憶體解釋器對程式碼的翻譯,從而大大加快整個過程?
(c) 如果我用等效
sed
或awk
構造替換 python 腳本,我懷疑整個事情會快得多。為什麼?是否可以輕鬆地進行這種算術運算,或者這是另一個需要注意的問題?編輯:不熟悉的人的範例輸出
dsmc
如下——僅當“普通文件”出現在字元串中時才會發送文件,然後是其大小(以字節為單位)。因此,在下面,文件spclicert.kdb
被發送,但既不發送TSM.PWD
也不發送目錄CaptiveNetworkSupport
:# dsmc incr / < header message containing personal information> Incremental backup of volume '/' ANS1898I ***** Processed 79,000 files ***** Directory--> 0 /Library/Preferences/SystemConfiguration/CaptiveNetworkSupport [Sent] Normal File--> 5,080 /Library/Preferences/Tivoli Storage Manager/Nodes/SHUG2765-MACBOOKPRO-PHYSICS/spclicert.kdb [Sent] Updating--> 224 /Library/Preferences/Tivoli Storage Manager/BrokenOrOld/TSM.PWD (original) [Sent]
所以,上面的腳本去掉了每個發送文件的字節大小,然後簡單地將它們相加。
假設連接是可靠的,一個簡單的組合就是使用使用者空間流量整形器。只需將其設置為使用不超過每天的最大頻寬。
l=$(( (200*10**6)/(24*60**2) )) trickle -d $l scp foo username@remotehost:~/
並且
trickle
會將傳輸速度減慢到每秒2314K,即每天最高不超過199,929,600,000字節。文件傳輸程序不必是scp
,它可以是任何東西,(甚至是一個網路瀏覽器),(或dsmc
),所以它是從命令行啟動的。這種方法的一個優點是,如果文件foo大於每日限制,則不需要分解文件。當然,發送foo需要一段時間,(如果foo是 1TB,則需要 5 天),但無論如何都要花那麼長時間。
trickle
有一個名為 的守護程序版本trickled
,它控制每一次後續執行的 `trickle. 例子:l=$(( (200*10**6)/(24*60**2) )) trickled -d $l trickle scp foo username@remotehost:~/ & trickle scp bar username@remotehost:~/ & trickle scp baz username@remotehost:~/ &
假設每個文件foo、bar和baz的大小為1TB,
trickled
仍將傳輸保持在200GB / 天的限制內。