有些业务脚本需要防止重复,以便造成业务数据不一致。
所以,一般在脚本运行开始,就首先要检查当前系统是否有该脚本的运行进程。
一般人多半是这样写的(比如我们系统里的那帮人):
processName="OMIGet_HOUR.sh"
processNum=`ps -aef |grep "${processName}" | wc -l`
if [ "${processNum}" -gt "3" ]; then
echo "已经有脚本在运行,本脚本不支持多实例运行${processNum}"
exit 1
fi
这样写有个问题,是什么呢?
ps -ef | grep 的返回结果在是否包含grep时具有不确定性(可以加上-v参数避免,但很麻烦)。所以稍不注意,判断就会失败。
其实我们可以使用更安全的一种方式来判断,如:
#!/bin/ksh
RUNDIR=`dirname $0`
PIDFILE="${RUNDIR}/$0.pid"
if [ -s ${PIDFILE} ]; then
echo "脚本已经在运行,不重复运行,退出."
exit 1
fi
echo $$ > ${PIDFILE}
<各种业务处理逻辑>
cat /dev/null > ${PIDFILE}
借助pidfile来判断,脚本在运行之初判断是否有进程号即可。
当然,如果脚本在运行之中被强制kill掉,那么下次怎么也运行不起来了。
不过这个问题,我们可以这样来解决:在脚本“假退出”之前,通过各种告警来提示维护人员(以手工介入处理)。
网上有很多关于在shell执行文件本身通过flock的命令加锁来实现互斥的方式,个人觉得,该方法不具有自助可控性,风险较大,最重要的是不具有平台通用性。
所以更加推荐如下方式:
#!/bin/ksh
RUNDIR=`dirname $0`
PIDFILE="${RUNDIR}/$0.pid"
if [ -s ${PIDFILE} ]; then
SPID=`cat ${PIDFILE}`
if [ -e /proc/${SPID}/status ]; then
echo "脚本已经在运行,不重复运行,退出."
exit 1
fi
cat /dev/null > ${PIDFILE}
fi
echo $$ > ${PIDFILE}
#各种业务逻辑
cat /dev/null > ${PIDFILE}
经过测试,该方法能够表现良好。