如何编写一个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!)。 如果退出状态不是 0until将运行循环体,在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文件的信息。 虽然他们很受欢迎, 他们也很有缺陷,没有理由不以正确的方式去做。

考虑这个:

  1. 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
  2. PID文件过时。 你需要过于复杂(或者我应该说,非平凡的)逻辑来检查PID文件是否过时,并且任何这样的逻辑再次容易受到1. .。

  3. 如果您甚至没有写入权限或处于只读环境,该怎么办?

  4. 这是毫无意义的过度复杂; 看看我上面的例子有多简单。 根本不需要复杂。

另请参阅: “正确”时,PID文件是否仍有缺陷?

顺便一提; 比PID文件更糟的是parsingps 千万不要这样做。

  1. ps是非常不可移植的。 几乎在每个UNIX系统上都可以find它; 如果你想要非标准的输出,它的参数变化很大。 标准输出仅供人类使用,不适用于脚本parsing!
  2. parsingps导致大量的误报。 以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。