背景描述
我们在机器上会部署一些定时任务,比如下面的每小时同步日志的任务:
50 * * * * /home/hduser/rsync.sh >> /home/hduser/cron.log
由于同步日志是阻塞式IO任务,偶尔出现任务重叠:
hduser 178955 21783 0 Jul27 08:50:00 bash rsync.sh 2020-07-27
hduser 178955 21783 0 Jul27 09:50:00 bash rsync.sh 2020-07-27
hduser 178955 21783 0 Jul27 10:50:00 bash rsync.sh 2020-07-27
当shell脚本中包含rsync或aws sync等阻塞式IO命令时,如何通过代码确保shell脚本是单例运行呢?
方法一:文件锁
#!/usr/bin/env bashFILELOCK=/tmp/file.lockif [ -e ${FILELOCK} ] && [ kill -0 `cat ${FILELOCK}` ];thenecho "${0} is running"exitfi#原先的脚本内容
上面的方法适用于上面的定时任务,确保时间只有一个任务运行。但是在多脚本竞争中会有异常,因为判断语句不是原子性的,也没有事务。
方法二:flock命令
针对多任务竞争的一般场景,linux提供了flock命令来实现文件锁的功能:
#!/usr/bin/env bashFILELOCK=/tmp/file.lock[ ${FILELOCK} != "${0}" ] && exec env FILELOCK="${0}" flock -en "$0" "$0" "$@";#原先的脚本内容
新的挑战:已有进程未正常运行
文件锁和flock命令无法解决任务未结束但是网络卡住的情况,我们在rsync命令中记录其传输速度:
#!/usr/bin/env bashrsync -avz --progress ${rsync_address} ${local_address};
可以观察到网速已经无法满足数据传输的需要了,需要关闭当前rsync进程,并重新建立新的网络链路:
${remote_file}
32768 1% 4.59KB/s 63:07:45
方法三:关闭已有进程
对于某些网络传输的定时任务,在指定时间段内未完成需要关闭已有进程,并开启新的进程来接替原来的任务:
#!/usr/bin/env bashps -ef|grep "${local_process}"|awk "NR==1{print ${2}"|xargs kill -9#原先的脚本内容
对于rysnc这样支持断点续传的命令,可以缩短时间间隔,从而提升同步任务的效率:
*/30 * * * * /home/hduser/rsync.sh >> /home/hduser/cron.log
对于aws s3 sync这样的不支持断点续传但本身支持重试传输的命令,可以加大时间间隔,从而提升同步任务的效率:
* */2 * * * /home/hduser/s3sync.sh >> /home/hduser/cron.log
总结
很多定时任务本身都是很快执行完的,无需考虑其单例执行的问题,但对于同步数据等含有阻塞式IO命令的定时脚本,需要确保其任意时刻仅有一个脚本在运行,而对于这种情况,我们给出了3种解决方案,并且对不同的数据传输类型该如何修改其任务时间间隔也给出了2个参考示例,对于使用调度器(如:airflow)的情况,后续也会分享一些确保任务单例运行的方法。
往期推荐:
【UE分享】微信小游戏中的运营拉新机会
【UI分享】关于奖励机制在游戏中的运用思考
【RD分享】WebAssembly——高性能Web的全新字节格式码
【UE分享】民族志和游戏用户研究的交织离合
【US分享】Golang中for语句的坑