在后台不断运行脚本:守护进程,使用crontab锁定文件,或者只是循环?

时间:2013-06-28 13:17:48

标签: perl daemon

我有一个

的Perl脚本
  • 在数据库中查询要处理的文件列表
  • 处理文件
  • 然后退出

启动时,此脚本会创建一个文件(比如说script.lock),退出后会删除该文件。我有一个crontab条目,每分钟运行一次这个脚本。如果存在lockfile,则脚本退出,假设其自身的另一个实例正在运行。

上述过程运行正常,但我对这种方法的稳健性不太满意。具体来说,如果由于某种原因脚本过早退出并且未删除锁定文件,则新实例将无法正常执行。

我希望对以下内容提出一些建议:

  1. 使用锁定文件是一种好方法还是有更好/更强大的方法来执行此操作?
  2. 使用crontab这个好主意还是我最好用sleep()写一个无限循环?
  3. 我应该使用GNU'守护程序'程序还是Perl Proc :: Daemon模块(或其他一些等效程序)吗?

3 个答案:

答案 0 :(得分:4)

我们假设您采用连续循环路线。你将你的程序重新调整为一个无限循环。你睡了一段时间,然后醒来并处理你的数据库文件,然后再回去睡觉。

您现在需要一种机制来确保您的程序仍然正常运行。这可以通过像inetd这样的东西来完成。

但是,您的程序基本上只执行一项任务,并且在一天中重复执行该任务。这就是crontab的用途。 inetd 机制适用于等待客户端的服务器,如https或sshd。在这些情况下,您需要一种机制来在服务器进程终止后立即重新创建它。

您可以改进 lockfile 机制的一种方法是将PID包含在其中。例如,在Perl脚本中执行此操作:

open my $lock_file_fh, ">", LOCK_FILE_NAME;
say {$lock_file_fh} "$$";
close $lock_file_fh;

现在,如果您的crontab看到锁定文件,它可以测试该进程ID是否仍在运行:

if [ -f $lock_file ]
then
    pid=$(cat $lock_file)
    if ! ps -p $pid
    then
        rm $lock_file
    fi
    restart_program
else
    restart_program
fi

答案 1 :(得分:3)

  1. 如果使用cron,使用锁文件是一个很好的方法,但如果你可以轻松安装和使用一个数据库,我会推荐一个数据库(MySQL / Postgres / whatever。不是SQLite)。除了其他原因之外,这比本地文件系统上的文件更便携,并且可以重复使用。

  2. 你确实是对的。因为你描述的原因,cron不是这个场景的最佳选择 - 如果过程过早死亡,很难恢复(你可以通过检查时间戳,但不是很容易)。

    你应该使用cron for是一个“start_if_daemon_died”工作。

  3. StackOverflow已经很好地涵盖了这一点,例如:这里:

    How can I run a Perl script as a system daemon in linux?”或more posts

答案 2 :(得分:1)

这并不是一个新的答案,而只是一个在David W的Perl中得到解决的例子。

my $LOCKFILE = '/tmp/precache_advs.lock';

create_lockfile();
do_something_interesting();
remove_lockfile();

sub create_lockfile {
    check_lockfile();

    open my $fh, ">", $LOCKFILE or die "Unable to open $LOCKFILE: $!";
    print $fh "$$";
    close $fh;

    return;
}

sub check_lockfile {
    if ( -e $LOCKFILE ) {
        my $pid = `cat $LOCKFILE`;
        if ( system("ps -p $pid") == 0 ) {
            # script is still running, so don't start a new instance
            exit 0;
        }
        else {
            remove_lockfile();
        }
    }
    return;
}

sub remove_lockfile {
    unlink $LOCKFILE or "Unable to remove $LOCKFILE: $!";
    return;
}