本随笔主要讲述在shell编程中实现任务并发处理。
一、调度脚本
#!/bin/sh
help()
{
echo "使用说明:"
echo " $0 子进程脚本 [slots]"
exit
}
if [ $# -lt 1 ]; then help; fi
#总任务数量
nJobs=4671
nSlots=${2:-8}
#设定工作目录
WORK_PATH=`pwd`
#设定工作脚本
work_script=$1
#actor下载地址
URL_ACTOR="https://www.meituri.com/t"
URL=$URL_ACTOR
#中断退出标志变量
exit_flag=0
#临时测试变量
flag=0
flag_set=1
exit_generate(){
if [ $exit_flag -eq 1 ]; then
echo "检测到重复操作,强制退出,会丢失保存数据"
exit 1
fi
exit_flag=1
echo "保存配置文件"
echo "flag=$flag flag_set=$flag_set cjob=$cjob nJobs=$nJobs" > config.ini
echo "等待子线程结束..."
while [ ${finish} -eq 0 ]; do
finish=1
idx=1
while [ ${idx} -le ${nSlots} ]; do
if [ -f $$_lock_${idx}.lck ]; then
finish=0
jobsDump ${idx}
else
rm -f "$$_dump_${idx}.dmp"
jobsDump ${idx} "本线程任务已结束..."
fi
idx=$((idx + 1))
done
sleep 1
done
echo "所有子线程已经全部结束,正常退出!"
exit 1
}
exit_2(){
echo "捕获中断信号:SIGINT"
exit_generate
}
exit_15(){
echo "捕获中断信号:SIGTERM"
exit_generate
}
#设置中断捕获响应
trap exit_2 2
trap exit_15 15
jobsInit()
{
clear
echo "$1"", ${nSlots} downloader"
idx=1
while [ $idx -le ${nSlots} ]; do
printf "\033[01;34mSlot %2d:\033[00m " $idx
idx=$((idx + 1))
done
}
jobsDump()
{
printf "\033[01;34mSlot %2d:\033[00m `date +%H:%M:%S` " $1
if [ -f $$_dump_$1.dmp ]; then
echo "`head -n1 $$_dump_$1.dmp`"
elif [ v"$2" != "v" ]; then
echo "$2"
fi
}
jobsInfo()
{
echo $*
}
if [ -f config.ini ]; then
echo "读取配置文件,恢复上一次的下载记录"
. ./config.ini
else
# clean dir
rm -rf Downloads
rm -f weblinklist.txt
cjob=0
fi
# Process jobs
finish=0
jobs_name="任务"
while [ ${finish} -eq 0 ]; do
jobsInfo "正在处理 ${cjob}/${nJobs} $jobs_name..."
finish=1
idx=1
while [ ${idx} -le ${nSlots} ]; do
if [ -f $$_lock_${idx}.lck ]; then
finish=0
jobsDump ${idx}
else
rm -f "$$_lock_${idx}.lck" "$$_dump_${idx}.dmp"
jobsDump ${idx} "空闲."
if [ ${cjob} -lt ${nJobs} ]; then
finish=0
#这里调度工作脚本,可以在这前面写逻辑生成调度脚本的参数
#这里通过新开shell来执行,防止调度脚本被中断后工作脚本一同遭殃
sh $work_script $$ ${idx} "$$_lock_${idx}.lck" "$$_dump_${idx}.dmp" $WORK_PATH $flag $URL $flag_set &
#创建/更新目录锁文件
#touch "$$_lock_${idx}.lck";
# Dump process info
jobsDump ${idx} "已分配任务,工作中..."
cjob=$((cjob + 1))
fi
fi
idx=$((idx + 1))
done
sleep 1
done
echo "所有任务已完成"
#删除创建的所有临时文件
rm -f $$_*
调度脚本主要负责调度用于执行真正工作任务的工作脚本,同时承担向工作脚本传不同的参数来向工作脚本下发不同的任务。
本脚本在原有参考的基础上进行了改进,新增中断捕获(两次中断强制退出),可以在结束任务的时候依旧保证各工作脚本的工作不被打断,同时监控各工作脚本的运行情况,此时不会再下发新任务,同时会保存当前的工作记录,可用于下一次启动的恢复上次工作的位置;修改了锁文件创建的逻辑,由被调度脚本创建并自行销毁,解决捕获中断可能造成的锁文件存在但工作任务未被调度的情况。
二、工作脚本
#!/bin/sh
# dowork.sh
# 调用方法和参数总结
# 该脚本被multi.sh调用执行,传递给脚本7-8个参数
# param 1: PID of multi.sh
# param 2: slot number
# param 3: lock file name
# param 4: info file name
# param 5: working dir
# param 6: working flag (set 0 to actorParser, set 1 to downloadParser)
#创建线程锁文件,标识线程运行态
touch "$3";
#配置文件
#wget下载参数配置
#重试次数:5,超时时间:5
tries=5
timeout=5
#宏参量定义
output=`pwd`/$4
#配置文件结束
#主程序起始点
#向output输出的信息可以被调度脚本所打印
echo "子进程接受下发任务,准备处理,工作标志flag=$6" > $output
# 在这上面可以写该脚本需要完成命令
echo "$2当前任务执行完毕,退出" > $output
# 脚本运行完了,在这里将锁文件删除,multi.sh就可以知道该脚本执行完毕了
rm -f $3
该脚本用于接受来自调度脚本下发的任务并运行,通过创建的info文件向调度脚本实时汇报自身工作状态。