我需要定期运行Perl脚本(〜每3-5分钟)。我想确保一次只能运行一个Perl脚本实例,所以下一个循环在上一个循环完成之前不会启动。可以/应该通过cron,Perl的某些内置功能来实现,还是我需要在脚本级别处理它?由cron运行只有一个Perl脚本实例
我对Perl和cron相当陌生,所以需要帮助和一般建议。
我需要定期运行Perl脚本(〜每3-5分钟)。我想确保一次只能运行一个Perl脚本实例,所以下一个循环在上一个循环完成之前不会启动。可以/应该通过cron,Perl的某些内置功能来实现,还是我需要在脚本级别处理它?由cron运行只有一个Perl脚本实例
我对Perl和cron相当陌生,所以需要帮助和一般建议。
我一直使用File::NFSLock得到的脚本本身的独占锁好运。
use Fcntl qw(LOCK_EX LOCK_NB);
use File::NFSLock;
# Try to get an exclusive lock on myself.
my $lock = File::NFSLock->new($0, LOCK_EX|LOCK_NB);
die "$0 is already running!\n" unless $lock;
这与其他锁文件建议的排序方式相同,只是除了尝试获取锁之外,没有其他任何操作。
如果脚本没有写入权限,这是否工作? – mob 2010-02-10 05:45:45
谢谢!我最喜欢它,因为它非常紧凑,不需要写任何文件。 – 2010-02-10 09:16:57
它仍在使用锁定文件。如果脚本是foo.pl,则默认使用foo.pl.NFSLock。所以你需要对目录有写权限。 – oylenshpeegul 2010-02-10 13:01:30
AFAIK perl没有这样的东西buildin。当您启动应用程序并删除它时,您可以轻松创建一个临时文件,当您的脚本完成时。
对不起,我预见带来很大麻烦,如果出现错误(机器重新启动)和文件不会被删除运行... – 2010-02-09 22:13:02
公平地说,但是如果机器在脚本运行时重新启动,那么您可能会遇到麻烦(除非脚本做了一些非常微不足道的事情)。 – 2010-02-09 22:33:07
在脚本运行期间可能发生重新启动。该脚本使用数据库事务,所以我认为它会“重新生存”重新启动,以保持数据的完整性。 – 2010-02-09 22:40:58
鉴于频率,我通常会编写一个守护进程(服务器),在作业运行之间很好地等待(即sleep()
),而不是尝试使用cron进行相当精细的访问。
如果有必要,在Unix/Linux系统上,您可以从/etc/inittab
(或更换)运行它,以确保它始终运行,并且在进程死亡或死亡时自动重启。
新增:(和一些无关紧要的东西去掉)
总是存在的(运行,但大多是空闲的)进程的方法具有消除脚本的并发实例的可能性的好处正在被由cron自动启动。
但是,这意味着您有责任正确管理计时,例如在存在重叠的情况下(例如,上次运行仍在运行,而新的触发器发生)。这可以帮助您决定是使用分叉守护进程还是非分叉设计。线程在这种情况下不提供任何优势,因此不需要考虑它们的用法。
这并不能完全消除多进程运行的可能性,但这是许多守护进程的常见问题。典型的解决方案是在文件上使用诸如互斥锁的信号量,以防止运行第二个实例。当进程结束时,文件锁自动被遗忘,因此在异常终止(例如电源故障)的情况下,锁自身不需要清理。
使用FCNTL模块,以及使用一个Perl sysopen
与O_EXCL
标志(或O_RDWR | O_CREAT | O_EXCL
)一种方法是given by Greg Bacon。我唯一的区别是将独占锁定结合到sysopen调用中(即使用我建议的标志),然后删除当时多余的调用。哦,我会遵循UNIX(& Linux FHS)文件系统和命名约定/var/run/daemonname.pid
。
另一种方法是使用djb的daemontools或similar来“守护”任务。
运行守护进程并不能消除多个运行脚本问题..对于某人或某事偶然尝试启动第二个守护进程很容易。你仍然需要一个PID文件来处理这个问题。 – mpounsett 2015-02-25 21:48:59
一个典型的方法是每个进程打开并锁定某个文件。然后该进程读取文件中包含的进程ID。
如果使用该ID的进程正在运行,那么后来者安静地退出。否则,新获胜者将其进程ID(Perl中的$$
)写入pidfile,关闭句柄(释放锁)并执行其业务。
举例如下实施:
#! /usr/bin/perl
use warnings;
use strict;
use Fcntl qw/ :DEFAULT :flock :seek /;
my $PIDFILE = "/tmp/my-program.pid";
sub take_lock {
sysopen my $fh, $PIDFILE, O_RDWR | O_CREAT or die "$0: open $PIDFILE: $!";
flock $fh => LOCK_EX or die "$0: flock $PIDFILE: $!";
my $pid = <$fh>;
if (defined $pid) {
chomp $pid;
if (kill 0 => $pid) {
close $fh;
exit 1;
}
}
else {
die "$0: readline $PIDFILE: $!" if $!;
}
sysseek $fh, 0, SEEK_SET or die "$0: sysseek $PIDFILE: $!";
truncate $fh, 0 or die "$0: truncate $PIDFILE: $!";
print $fh "$$\n" or die "$0: print $PIDFILE: $!";
close $fh or die "$0: close: $!";
}
take_lock;
print "$0: [$$] running...\n";
sleep 2;
使用File::Pid存储脚本的PID的文件,该脚本应在开始时检查,如果发现异常中止。当剧本完成后,你可以删除pidfile进程文件,但它不是真正需要的,你可以简单地检查后,看是否该进程ID仍然活着(这也将占到情况下,当你的脚本中意外中断):
use strict;
use warnings;
use File::Pid;
my $pidfile = File::Pid->new({file => /var/run/myscript});
exit if $pidfile->running();
$pidfile->write();
# ... rest of script...
# end of script
$pidfile->remove();
exit;
Sys :: RunAlone模块可以很好地完成你想要的功能。只需添加
use Sys::RunAlone;
靠近代码顶部。
+1。 – moodboom 2015-02-03 22:54:00
我一直使用这个 - 小而简单 - 不依赖于任何模块,并且可以同时运行Windows + Linux。
use Fcntl ':flock';
### Check to make sure there is only one instance ###
open SELF, "< $0" or die("Cannot run two instances of this program");
unless (flock SELF, LOCK_EX | LOCK_NB) {
print "You cannot run two instances of this program , a process is still running";
exit 1;
}
哪个平台,你在说什么? * nix中? – weismat 2010-02-09 22:27:46
它会在Linux上 – 2010-02-09 22:32:31
http://blog.booking.com/highlander-daemons-without-daemons.html – stu42j 2014-04-18 17:22:21