IT序号网

什么Shell也能并行化知识解答

qq123 2021年05月25日 程序员 225 0

作为一名后台开发,写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


发布评论
IT序号网

微信公众号号:IT虾米 (左侧二维码扫一扫)欢迎添加!

日常运维--rsync同步工具知识解答
你是第一个吃螃蟹的人
发表评论

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。