2010-02-09 26 views
20

我需要定期运行Perl脚本(〜每3-5分钟)。我想确保一次只能运行一个Perl脚本实例,所以下一个循环在上一个循环完成之前不会启动。可以/应该通过cron,Perl的某些内置功能来实现,还是我需要在脚本级别处理它?由cron运行只有一个Perl脚本实例

我对Perl和cron相当陌生,所以需要帮助和一般建议。

+0

哪个平台,你在说什么? * nix中? – weismat 2010-02-09 22:27:46

+0

它会在Linux上 – 2010-02-09 22:32:31

+0

http://blog.booking.com/highlander-daemons-without-daemons.html – stu42j 2014-04-18 17:22:21

回答

14

我一直使用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; 

这与其他锁文件建议的排序方式相同,只是除了尝试获取锁之外,没有其他任何操作。

+0

如果脚本没有写入权限,这是否工作? – mob 2010-02-10 05:45:45

+0

谢谢!我最喜欢它,因为它非常紧凑,不需要写任何文件。 – 2010-02-10 09:16:57

+1

它仍在使用锁定文件。如果脚本是foo.pl,则默认使用foo.pl.NFSLock。所以你需要对目录有写权限。 – oylenshpeegul 2010-02-10 13:01:30

1

AFAIK perl没有这样的东西buildin。当您启动应用程序并删除它时,您可以轻松创建一个临时文件,当您的脚本完成时。

+2

对不起,我预见带来很大麻烦,如果出现错误(机器重新启动)和文件不会被删除运行... – 2010-02-09 22:13:02

+0

公平地说,但是如果机器在脚本运行时重新启动,那么您可能会遇到麻烦(除非脚本做了一些非常微不足道的事情)。 – 2010-02-09 22:33:07

+0

在脚本运行期间可能发生重新启动。该脚本使用数据库事务,所以我认为它会“重新生存”重新启动,以保持数据的完整性。 – 2010-02-09 22:40:58

-1

鉴于频率,我通常会编写一个守护进程(服务器),在作业运行之间很好地等待(即sleep()),而不是尝试使用cron进行相当精细的访问。

如果有必要,在Unix/Linux系统上,您可以从/etc/inittab(或更换)运行它,以确保它始终运行,并且在进程死亡或死亡时自动重启。

新增:(和一些无关紧要的东西去掉)

总是存在的(运行,但大多是空闲的)进程的方法具有消除脚本的并发实例的可能性的好处正在被由cron自动启动。

但是,这意味着您有责任正确管理计时,例如在存在重叠的情况下(例如,上次运行仍在运行,而新的触发器发生)。这可以帮助您决定是使用分叉守护进程还是非分叉设计。线程在这种情况下不提供任何优势,因此不需要考虑它们的用法。

这并不能完全消除多进程运行的可能性,但这是许多守护进程的常见问题。典型的解决方案是在文件上使用诸如互斥锁的信号量,以防止运行第二个实例。当进程结束时,文件锁自动被遗忘,因此在异常终止(例如电源故障)的情况下,锁自身不需要清理。

使用FCNTL模块,以及使用一个Perl sysopenO_EXCL标志(或O_RDWR | O_CREAT | O_EXCL)一种方法是given by Greg Bacon。我唯一的区别是将独占锁定结合到sysopen调用中(即使用我建议的标志),然后删除当时多余的调用。哦,我会遵循UNIX(& Linux FHS)文件系统和命名约定/var/run/daemonname.pid

另一种方法是使用djb的daemontoolssimilar来“守护”任务。

+0

运行守护进程并不能消除多个运行脚本问题..对于某人或某事偶然尝试启动第二个守护进程很容易。你仍然需要一个PID文件来处理这个问题。 – mpounsett 2015-02-25 21:48:59

3

一个典型的方法是每个进程打开并锁定某个文件。然后该进程读取文件中包含的进程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; 
11

使用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; 
+0

我很惊讶地发现没有鸡群来源! – 2010-02-09 22:44:37

+0

@gbacon:随时提交补丁:) – Ether 2010-02-09 22:58:07

+0

我不会推荐这个答案。你有一个竞争条件(运行后调用write)。这可能永远不会成为cron工作中的一个问题,但仍然可以避免,因为有更好的答案(请参阅已接受的答案)。 – Calimo 2016-06-17 07:36:20

11

Sys :: RunAlone模块可以很好地完成你想要的功能。只需添加

use Sys::RunAlone; 

靠近代码顶部。

+0

+1。 – moodboom 2015-02-03 22:54:00

1

我一直使用这个 - 小而简单 - 不依赖于任何模块,并且可以同时运行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; 
}