Python

加速重複的 python 呼叫(或者,將復雜的正則表達式移植到 sed)

  • January 14, 2019

我是一名學術醫學物理學家。我做的實驗會產生大量數據,而且執行起來很昂貴。我的大學有一個備份系統,它由一個廢棄鹽礦中的機器人磁帶庫組成,它使用 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) 如果我用等效sedawk構造替換 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]

所以,上面的腳本去掉了每個發送文件的字節大小,然後簡單地將它們相加。

假設連接是可靠的,一個簡單的組合就是使用使用者空間流量整形器。只需將其設置為使用不超過每天的最大頻寬。

使用trickle, 一個大文件foo和的範例scp

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:~/ &

假設每個文件foobarbaz的大小為1TBtrickled仍將傳輸保持在200GB / 天的限制內。

引用自:https://unix.stackexchange.com/questions/494317