什么?Shell也能並行化


作為一名后台開發,寫shell腳本可能是工作中避免不了的,比如日志分析過濾、批量請求和批量插入數據等操作,這些如果單純靠人工手動去處理既費時又費力,有了shell腳本就可以輕松搞定,當然有人會說可以用python或者其他編程語言,這並不是不可以,但沒有哪個有shell這么簡單方便快捷的。需要依賴庫不說,還要懂對應語言的語法才行。

不知道大家在工作中有沒有經常會遇到測試或者產品跑過來說要在數據庫批量插入大量的樣本數據,在下就經常遇到,比如昨天測試同學就找上門需要對一批用戶調用http接口進行一系列操作,很自然想到對用戶文件進行while循環讀取然后調用curl命令發送http請求,於是寫出如下腳本代碼:

#! /bin/sh
#$1 為用戶列表文件,以參數形式傳入
cat $1 | while read line
do curl
"http://gztest.gf.com.cn:8000/otcblueup/client_id=010100047752&prod_code=${line}&occur_amount=100000&env_no=146" done

 

這樣寫有沒有問題?功能性的問題自然是沒有,但是存在性能問題,實際的運行效果是這樣的。

什么?Shell也能並行化

效果圖一

命令是串行執行的,批量處理的場景往往數據量比較大,再加上中間的網絡延時,往往需要等待很長時間才能徹底執行完成【如果願意等也無所謂】。作為程序員對技術的追求是極致的,學過那么多語言的並發技能(進程、線程、協程、異步多路復用等),shell腳本是否也可以進行並發呢?對上面的串行代碼進行修改如下:

#! /bin/sh#
$1 為用戶列表文件,以參數形式傳入
cat $1 | while read line
do    
    {        
        echo $line        
        curl "http://gztest.gf.com.cn:8000/otcblueup/client_id=010100047752&prod_code=${line}&occur_amount=100000&env_no=146"    
    }& 
#{}中的命令將以新的子進程中執行而不阻塞父進程
done        

 

&語法意思是將{}中的代碼將作為一個整體切換到后端運行而不阻塞下一次循環,再次執行的效果如下圖。

什么?Shell也能並行化

效果圖二

可以看到命令瞬間執行完成。並行化雖然是實現了,但是可以發現shell主進程已經執行完了,才看到子進程的打印,這是因為父進程沒有等待子進程完成就退出了。

進一步思考,如果存在任務串行依賴應該怎么做呢?比如有第二步操作需要等待上面的http請求執行完才能執行的需求,應該怎么去改進腳本呢?想到unix下的wait函數,同樣shell也有對應的wait命令可用,再次對腳本修改如下。

#!/bin/sh
cat $1 | while read line
do    
    {        
        curl "http://gztest.gf.com.cn:8000/otcblueup/client_id=010100047752&prod_code=${line}&occur_amount=100000&env_no=146"    
    }& 
#{}中的命令將以新的子進程中執行而不阻塞父進程
done
echo "wait all task finish,then exit"
wait
echo "success"    

 

什么?Shell也能並行化

效果圖三

執行后wait似乎沒有生效,"wait all task finish,then exit"以及"success"在后台進程完成前就輸出了,這是因為while循環的輸入來自於cat輸出到管道中的數據,wait實際等待的是cat命令的結束,用重定向的方式將cat文件的內容傳給while循環可解決此問題。

#!/bin/sh
while read line
do   
     {       
         curl "http://gztest.gf.com.cn:8000/otcblueup/client_id=010100047752&prod_code=${line}&occur_amount=100000&env_no=146"    
    }& 
#{}中的命令將以新的子進程中執行而不阻塞父進程
done < $1 
#重定向
echo "wait all task finish,then exit!!!"
wait
echo "success"    

 

下面截圖展示符合預期的結果,wait等待了所有后台進程完成最后輸出success標識。

什么?Shell也能並行化

效果圖四

更進一步,上面的並發是一次性啟動了所有任務,這對於機器資源以及性能會有很大影響,有沒有什么方式可以控制shell進程並發度呢?這里介紹一種利用管道進行並發控制方式。用mkfifo創建first in first out管道,控制並發邏輯如下。

#!/bin/bash
#並發數
threadTask=2
#創建fifo管道
fifoFile="test_fifo"
rm -f ${fifoFile}
mkfifo ${fifoFile}
# 建立文件描述符關聯
exec 9<> ${fifoFile}
rm -f ${fifoFile}
# 預先向管道寫入數據
for ((i=0;i<${threadTask};i++))
do    
    echo "" >&9
done
echo "wait all task finish,then exit!!!"
while read line
    do    
        read -u9    
        {        
            curl "http://gztest.gf.com.cn:8000/otcblueup/client_id=010100047752&prod_code=${line}&occur_amount=100000&env_no=146"       
            echo "" >&9    
         }&
 #{}中的命令將以新的子進程中執行而不阻塞父進程
done < $1
wait
# 關閉管道
exec 9>&-
echo
echo "success"    

 

並發控制效果如下,並發度設置為2【這樣並發控制效果明顯】,可以看到一次性啟動2個進程任務,每完成一個會重新啟動一個新的進程直到所有進程任務完成。

什么?Shell也能並行化

效果圖五

總結:

shell腳本簡單實用,如果能多多掌握一些shell技巧,將大大提高后端人員日常工作效率。從而減少無效加班,降低代碼工作者996.ICU風險。

轉自https://www.toutiao.com/a6773190691846619661/?timestamp=1577336958&app=news_article&group_id=6773190691846619661&req_id=201912261309170100140400961B1091A0

喜歡這篇文章?歡迎打賞~~

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM