276

我有一个 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
4

10 回答 10

722

避免使用 PID 文件、crons 或任何其他试图评估不是其子进程的进程。

在 UNIX 中,您只能等待您的孩子,这是有充分理由的。任何试图解决这个问题的方法(ps 解析、pgrep、存储 PID,...)都是有缺陷的,并且存在漏洞。只是说不

相反,您需要将监控您的进程的进程作为该进程的父进程。这是什么意思?这意味着只有启动你的进程的进程才能可靠地等待它结束。在 bash 中,这绝对是微不足道的。

until myserver; do
    echo "Server 'myserver' crashed with exit code $?.  Respawning.." >&2
    sleep 1
done

上面这段 bash 代码循环运行myserveruntil第一行开始myserver并等待它结束。当它结束时,until检查它的退出状态。如果退出状态为0,则表示它正常结束(这意味着您要求它以某种方式关闭,并且它成功地关闭了)。在这种情况下,我们不想重新启动它(我们只是要求它关闭!)。如果退出状态不是 0,将运行循环体,它会在 STDERR 上发出错误消息并在 1 秒后until重新启动循环(回到第 1 行)。

我们为什么要等一秒钟?因为如果启动顺序有问题myserver,它会立即崩溃,你将有一个非常密集的循环,不断重启和崩溃。消除了sleep 1压力。

现在您需要做的就是启动这个 bash 脚本(可能是异步的),它会监控myserver并在必要时重新启动它。如果您想在启动时启动监视器(使服务器“存活”重新启动),您可以使用@reboot规则将其安排在用户的 cron(1) 中。使用以下命令打开您的 cron 规则crontab

crontab -e

然后添加一个规则来启动你的监控脚本:

@reboot /usr/local/bin/myservermonitor

或者; 查看 inittab(5) 和 /etc/inittab。您可以在其中添加一行以myserver从某个初始级别开始并自动重生。


编辑。

让我添加一些关于为什么使用 PID 文件的信息。虽然它们很受欢迎;他们也有很大的缺陷,你没有理由不以正确的方式去做。

考虑一下:

  1. PID回收(杀错进程):

    • /etc/init.d/foo start: 开始foo,将fooPID写入/var/run/foo.pid
    • 过了一会儿:foo不知何故死去。
    • 过了一会儿:任何开始的随机进程(称为它bar)都需要一个随机 PID,想象一下它使用foo的是旧的 PID。
    • 你注意到foo's''''''''''''''''''''''''''''''''''''''''''''''/etc/init.d/foo/restart读取,检查它/var/run/foo.pid是否还活着,找到' ' ''''''''''''''''''''''''''''barfoofoo
  2. PID 文件过时。您需要过于复杂(或者我应该说,不平凡)的逻辑来检查 PID 文件是否过时,并且任何此类逻辑再次容易受到1..

  3. 如果您甚至没有写访问权限或处于只读环境中怎么办?

  4. 这是毫无意义的过度复杂化;看看我上面的例子是多么简单。完全没有必要把它复杂化。

另请参阅:“正确”执行时,PID 文件是否仍然存在缺陷?

顺便一提; 比 PID 文件更糟糕的是解析ps 永远不要这样做。

  1. ps非常不便携。虽然您几乎可以在每个 UNIX 系统上找到它;如果您想要非标准输出,它的参数会有很大差异。标准输出仅供人类使用,不能用于脚本解析!
  2. 解析ps会导致很多误报。举ps aux | grep PID个例子,现在想象有人以某个地方的数字作为参数启动一个进程,该参数恰好与您盯着守护程序使用的 PID 相同!想象一下,两个人开始一个 X 会话,而你正在寻找 X 来杀死你的会话。这只是各种各样的坏事。

如果您不想自己管理流程;有一些非常好的系统可以作为您流程的监控器。例如,查看runit

于 2009-03-30T12:53:53.950 回答
45

看看 monit ( http://mmoni.com/monit/ )。它处理脚本的启动、停止和重新启动,并且可以在必要时进行健康检查和重新启动。

或者做一个简单的脚本:

while true
do
/your/script
sleep 1
done
于 2009-03-30T12:39:02.207 回答
11

最简单的方法是在文件中使用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 中,您实际上可以测试它是否正在运行:

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

但是当然你不必测试,因为如果它已经在运行并且你重新启动它,它会退出'other instance already running'

当进程终止时,它的所有文件描述符都将关闭,并且所有锁都会自动删除。

于 2009-03-30T11:32:47.250 回答
6

您应该使用 monit,这是一个标准的 unix 工具,可以监控系统上的不同事物并做出相应的反应。

来自文档: http: //mmonit.com/monit/documentation/monit.html#pid_testing

使用 pidfile /var/run/checkqueue.pid 检查进程 checkqueue.py
       如果更改了 pid 然后 exec "checkqueue_restart.sh"

您还可以将 monit 配置为在重启时向您发送电子邮件。

于 2009-03-30T12:19:59.727 回答
5
if ! test -f $PIDFILE || ! psgrep `cat $PIDFILE`; then
    restart_process
    # Write PIDFILE
    echo $! >$PIDFILE
fi
于 2009-03-30T11:34:05.297 回答
5

排队:

while true; do <your-bash-snippet> && break; done

例如#1

while true; do openconnect x.x.x.x:xxxx && break; done

例如#2

while true; do docker logs -f container-name; sleep 2; done
于 2020-11-25T08:36:53.163 回答
3

我不确定它在操作系统之间的可移植性如何,但您可以检查您的系统是否包含“run-one”命令,即“man run-one”。具体来说,这组命令包括“run-one-constantly”,这似乎正是需要的。

从手册页:

不断运行命令 [ARGS]

注意:显然这可以从你的脚本中调用,但它也完全不需要脚本。

于 2018-10-22T00:00:46.767 回答
2
watch "yourcommand"

如果/当它停止时(延迟 2 秒后),它将重新启动该过程。

watch -n 0.1 "yourcommand"

在 0.1 秒后重新启动它而不是默认的 2 秒

watch -e "yourcommand"

如果程序出现错误退出,则停止重新启动。

好处:

  • 内置命令
  • 一条线
  • 易于使用和记忆。

缺点:

  • 仅在完成后在屏幕上显示命令的结果
于 2021-11-10T09:20:27.613 回答
1

我在众多服务器上使用了以下脚本并取得了巨大成功:

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包含足够多的流程路径,这是完全明确的
  • 在等待进程死亡时使用睡眠,避免占用资源:)

该脚本实际上用于关闭正在运行的 tomcat 实例,我想在命令行中关闭(并等待)它,因此将它作为子进程启动根本不适合我。

于 2010-05-24T09:47:19.750 回答
1

我将它用于我的 npm 进程

#!/bin/bash
for (( ; ; ))
do
date +"%T"
echo Start Process
cd /toFolder
sudo process
date +"%T"
echo Crash
sleep 1
done
于 2019-12-21T06:03:21.763 回答