(转)什么?Shell也能并行化
原文:https://www.toutiao.com/article/6773190691846619661/?timestamp=1577336958&app=news_article&group_id=6773190691846619661&req_id=201912261309170100140400961B1091A0
作为一名后台开发,写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脚本是否也可以进行并发呢?对上面的串行代码进行修改如下:
#! /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主进程已经执行完了,才看到子进程的打印,这是因为父进程没有等待子进程完成就退出了。
进一步思考,如果存在任务串行依赖应该怎么做呢?比如有第二步操作需要等待上面的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"
效果图三
执行后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进程并发度呢?这里介绍一种利用管道进行并发控制方式。用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技巧,将大大提高后端人员日常工作效率。从而减少无效加班,降低代码工作者996.ICU风险。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战