如何编写一个bash脚本来重新启动进程,如果它死了?
我有一个Python脚本,将检查一个队列,并对每个项目执行一个操作:
# checkqueue.py while True: check_queue() do_something()
如何编写一个bash脚本来检查它是否正在运行,如果没有,就启动它。 大致下面的伪代码(或者它应该做类似ps | grep
?的东西):
# keepalivescript.sh if processidfile exists: if processid is running: exit, all ok run checkqueue.py write processid to processidfile
我会从crontab中调用它:
# crontab */5 * * * * /path/to/keepalivescript.sh
避免PID文件,cron或其他任何试图评估不是子女的进程。
有一个很好的理由,在UNIX中,你只能等待你的孩子。 任何试图解决这个问题的方法(psparsing,pgrep,存储一个PID,…)都是有缺陷的,并且有漏洞。 只是说不。
相反,您需要将监控stream程的stream程作为stream程的父级。 这是什么意思? 这意味着只有开始你的过程的过程能够可靠地等待它结束。 在bash中,这是绝对微不足道的。
until myserver; do echo "Server 'myserver' crashed with exit code $?. Respawning.." >&2 sleep 1 done
上面myserver
bash代码在一个until
循环中运行myserver
。 第一行启动myserver
并等待它结束。 当它结束时, until
检查其退出状态。 如果退出状态为0
,则意味着它正常结束(这意味着您要求它closures,并成功完成)。 在这种情况下,我们不想重新启动它(我们只是要求它closures!)。 如果退出状态不是 0
, until
将运行循环体,在STDERR上发出错误消息,并在1秒后重新启动循环(回到第1行)。
我们为什么要等一下? 因为如果myserver
的启动顺序出现问题,并且立即崩溃,那么您将有一个非常密集的循环,不断重启并崩溃。 sleep 1
消除了这种压力。
现在,您只需启动这个bash脚本(可能是asynchronous的),它将监视myserver
并根据需要重新启动它。 如果要在启动时启动监视器(使服务器“重新启动”),则可以使用@reboot
规则将其安排在用户的cron(1)中。 用crontab
打开你的cron规则:
crontab -e
然后添加一条规则来启动你的监视脚本:
@reboot /usr/local/bin/myservermonitor
另外; 看一下inittab(5)和/ etc / inittab。 你可以在那里添加一行,让myserver
在某个初始级别启动并自动重新生成。
编辑。
让我添加一些关于为什么不使用PID文件的信息。 虽然他们很受欢迎, 他们也很有缺陷,没有理由不以正确的方式去做。
考虑这个:
-
PID回收(查错过程):
-
/etc/init.d/foo start
:启动foo
,把foo
的PID写到/var/run/foo.pid
- 一会儿:
foo
不知何故死亡。 - 一段时间后:任何随机的进程开始(称之为
bar
)需要一个随机PID,想象它采用了foo
的旧PID。 - 你注意到
foo
已经消失了:/etc/init.d/foo/restart
读取/var/run/foo.pid
,检查它是否还活着,findbar
,认为是foo
,杀死它,开始一个新的foo
。
-
-
PID文件过时。 你需要过于复杂(或者我应该说,非平凡的)逻辑来检查PID文件是否过时,并且任何这样的逻辑再次容易受到
1.
.。 -
如果您甚至没有写入权限或处于只读环境,该怎么办?
-
这是毫无意义的过度复杂; 看看我上面的例子有多简单。 根本不需要复杂。
另请参阅: “正确”时,PID文件是否仍有缺陷?
顺便一提; 比PID文件更糟的是parsingps
! 千万不要这样做。
-
ps
是非常不可移植的。 几乎在每个UNIX系统上都可以find它; 如果你想要非标准的输出,它的参数变化很大。 标准输出仅供人类使用,不适用于脚本parsing! - parsing
ps
导致大量的误报。 以ps aux | grep PID
ps aux | grep PID
例子,现在想象一下,有人开始一个进程的数字作为参数,恰好与你守护进程的PID一样! 想象一下,两个人开始一个X会话,并且为了X而杀了你。 这只是各种不好的事情。
如果你不想自己pipe理这个过程, 有一些非常好的系统可以作为你的程序的监视器。 例如,看看runit 。
看看monit( http://mmonit.com/monit/ )。 它处理脚本的启动,停止和重新启动,并且可以进行健康检查,并在必要时重新启动。
或者做一个简单的脚本
while 1 do /your/script sleep 1 done
最简单的方法是在文件上使用flock。 在Python脚本中你会这样做
lf = open('/tmp/script.lock','w') if(fcntl.flock(lf, fcntl.LOCK_EX|fcntl.LOCK_NB) != 0): sys.exit('other instance already running') lf.write('%d\n'%os.getpid()) lf.flush()
在shell中,你可以testing它是否正在运行:
if [ `flock -xn /tmp/script.lock -c 'echo 1'` ]; then echo 'it's not running' restart. else echo -n 'it's already running with PID ' cat /tmp/script.lock fi
但是,当然,您不必testing,因为如果它已经在运行,并且您重新启动它,它将以'other instance already running'
当进程死亡时,所有的文件描述符都closures,所有的锁都自动删除。
if ! test -f $PIDFILE || ! psgrep `cat $PIDFILE`; then restart_process # Write PIDFILE echo $! >$PIDFILE fi
你应该使用monit,这是一个标准的unix工具,可以监视系统上的不同事物并作出相应的反应。
从文档: http : //mmonit.com/monit/documentation/monit.html#pid_testing
使用pidfile /var/run/checkqueue.pid检查进程checkqueue.py 如果更改了pid,那么exec“checkqueue_restart.sh”
你也可以configuration监控,当它重新启动时给你发邮件。
我已经在众多的服务器上使用了以下脚本,
pid=`jps -v | grep $INSTALLATION | awk '{print $1}'` echo $INSTALLATION found at PID $pid while [ -e /proc/$pid ]; do sleep 0.1; done
笔记:
- 它正在寻找一个java进程,所以我可以使用jps,这在分布上比ps更加一致
-
$INSTALLATION
包含足够的进程path,这是完全明确的 - 在等待死亡的过程中使用睡眠,避免占用资源:)
这个脚本实际上是用来closures正在运行的tomcat实例的,我想在命令行closures(并等待),所以启动它作为一个subprocess根本不是我的select。