我正在设置一个 cron 作业,作为我创建的一个守护进程的一种看门狗。如果守护进程出错并失败,我希望 cron 作业周期性地重新启动它... ... 我不确定这是否可能,但我通读了一些 cron 教程,找不到任何可以实现我所寻找的功能的东西... ..。
我的守护进程是从一个 shell 脚本启动的,所以我实际上只是在寻找一种方法来运行 cron 作业,如果该作业的前一次运行不再运行的话。
我发现了这篇文章 ,它确实为我尝试使用锁文件做的事情提供了一个解决方案,但我不确定是否有更好的方法..。
不要尝试通过 cron 进行操作。让 cron 无论如何都要运行一个脚本,然后让脚本决定程序是否正在运行,并在必要时启动它(注意,你可以使用 Ruby 或 Python 或者你喜欢的脚本语言来做这件事)
我为我写的一个 print 假脱机程序做了这个,它只是一个 shell 脚本:
#!/bin/sh if ps -ef | grep -v grep | grep doctype.php ; then exit 0 else /home/user/bin/doctype.php >> /home/user/bin/spooler.log & #mailing program /home/user/bin/simplemail.php "Print spooler was not running... Restarted." exit 0 fi
它每两分钟运行一次,非常有效。如果由于某种原因进程没有运行,我让它用特殊信息给我发电子邮件。
作为 Earlz 答案的后续,您需要一个包装器脚本,它在启动时创建一个 $PID.running 文件,在结束时删除该文件。包装器脚本调用要运行的脚本。包装器是必要的,以防目标脚本失败或出错,pid 文件被删除。.
我建议使用一个现有的工具,如 Monit,它将监视和自动重新启动过程。有更多的信息可用 给你。它在大多数发行版中应该很容易获得。
正如其他人所说,编写和检查 PID 文件是一个很好的解决方案:
#!/bin/bash mkdir -p "$HOME/tmp" PIDFILE="$HOME/tmp/myprogram.pid" if [ -e "${PIDFILE}" ] && (ps -u $(whoami) -opid= | grep -P "^\s*$(cat ${PIDFILE})$" &> /dev/null); then echo "Already running." exit 99 fi /path/to/myprogram > $HOME/tmp/myprogram.log & echo $! > "${PIDFILE}" chmod 644 "${PIDFILE}"
您也可以直接在 crontab 中将其作为一行程序执行:
* * * * * [ `ps -ef|grep -v grep|grep <command>` -eq 0 ] && <command>
考虑使用 pgrep (如果可用的话)而不是通过 grep 传输的 ps,如果您要这样做的话。不过,就我个人而言,我已经从表单的脚本中获得了很多经验
while(1){ call script_that_must_run sleep 5 }
虽然这可能会失败和 cron 作业 是往往是最好的方式为基本的东西。只是另一种选择。
作为对 Rsanden 的回答的改进,我建议如下(我会发表评论,但是没有足够的声誉... ...) :
#!/usr/bin/env bash PIDFILE="$HOME/tmp/myprogram.pid" if [ -e "${PIDFILE}" ] && (ps -p $(cat ${PIDFILE}) > /dev/null); then echo "Already running." exit 99 fi /path/to/myprogram
这样可以避免可能的错误匹配(以及 greping 的开销) ,并且它抑制输出,仅依赖于 ps 的退出状态。
使用 lockrun,您不需要为 cron 作业编写包装器脚本
lockrun
使用 flock。这是新的。它更好。
flock
现在您不必自己编写代码了
/usr/bin/flock -n /tmp/my.lockfile /usr/local/bin/my_script
简单的自定义 php 就足够了,不需要与 shell 脚本混淆。
让我们假设您想要运行 /home/mypath/example.php,如果不运行
然后使用下面的自定义 php 脚本执行相同的工作。
创建以下 /home/mypath/forever. php
<?php $cmd = $argv[1]; $grep = "ps -ef | grep '".$cmd."'"; exec($grep,$out); if(count($out)<5){ $cmd .= ' > /dev/null 2>/dev/null &'; exec($cmd,$out); print_r($out); } ?>
然后在 cron 中添加以下内容
* * * * * php /home/mypath/forever.php 'php /home/mypath/example.php'
令人惊讶的是没有人提到 第一轮。我已经用它解决了我的问题。
apt-get install run-one
然后在 crontab 脚本之前添加 run-one
run-one
*/20 * * * * * run-one python /script/to/run/awesome.py
查看 这个 askubuntu SE 的答案。你也可以在那里找到详细信息的链接。
当我运行 php 脚本时,我是这样做的:
* * * * * php /path/to/php/script.php &
<?php if (shell_exec('ps aux | grep ' . __FILE__ . ' | wc -l') > 1) { exit('already running...'); } // do stuff
该命令正在系统进程列表中搜索当前的 php 文件名 如果存在,则行计数器(wc-l)将大于1,因为搜索命令本身包含文件名
因此,如果您运行 php crons,将上面的代码添加到您的 php 代码的开始,它将只运行一次。
这句话从来没有让我失望过:
返回文章页面一个人译者:
LFILE=/tmp/one-`echo "$@" | md5sum | cut -d\ -f1`.pid if [ -e ${LFILE} ] && kill -0 `cat ${LFILE}`; then exit fi trap "rm -f ${LFILE}; exit" INT TERM EXIT echo $$ > ${LFILE} $@ rm -f ${LFILE}
Cron job :
* * * * * /path/to/one.sh <command>
# one instance only (works unless your cmd has 'grep' in it) ALREADY_RUNNING_EXIT_STATUS=0 bn=`basename $0` proc=`ps -ef | grep -v grep | grep "$bn" | grep -v " $$ "` [ $? -eq 0 ] && { pid=`echo $proc | awk '{print $2}'` echo "$bn already running with pid $pid" exit $ALREADY_RUNNING_EXIT_STATUS }
更新. . 使用羊群的更好方法:
/usr/bin/flock -n /tmp/your-app.lock /path/your-app args
医生: https://www.timkay.com/solo/
独奏是一个非常简单的脚本(10行) ,防止程序从 一次运行多个副本 确保作业在前一个作业完成之前不会运行。
例子
* * * * * solo -port=3801 ./job.pl blah blah