我需要升级Perl CGI脚本,用户必须完成3个步骤.每个步骤完成后,脚本将记录用户完成的步骤.记录这一点很重要,所以我们可以向用户证明他们只是完成了第一步,没有完成所有三个步骤,例如.
现在,脚本正在为CGI脚本的每个实例创建1个日志文件.因此,如果UserA执行步骤1,则UserB执行步骤1,然后步骤2,然后步骤3 – 然后UserA完成步骤2和步骤3日志文件的顺序.
LogFile.UserA.Step1 LogFile.UserB.Step1 LogFile.UserB.Step2 LogFile.UserB.Step3 LogFile.UserA.Step2 LogFile.UserA.Step3
这样做可以防止同一个文件被写入不止一次,但目录可以快速获得数千个文件(每个文件只包含几个字节).有一个旋转和压缩这些日志的过程,但是它已经落在我身上,使脚本每天只记录一个文件,以减少正在创建的日志文件的数量.
基本上,日志文件将具有文件名中的当前日期,并且CGI脚本需要写入日志的任何时间都将附加到该日期的一个日志文件中,无论用户是什么,还是进行什么步骤.
没有什么需要阅读日志文件 – 唯一会发生的事情是CGI脚本的追加.日志轮换将在7天或更长时间的日志文件上运行.
我的问题是,处理并发附加到这个日志文件的最好方法是什么?我需要锁定吗?我在Perl Monks上发现了this page,似乎表明“当多个进程正在写入同一个文件,并且所有这些文件都打开以附加文件时,数据不会被覆盖.
我知道,只是因为可以做到并不意味着我应该,但在这种情况下,什么是最安全的最佳做法呢?
概要:
>并发附加到同一个文件
>每个附加到文件只是一行,少于50个字符
>订单没关系
谢谢!
解决方法
是的,使用羊群.
下面是一个示例程序,从典型的前沿开始:
#! /usr/bin/perl use warnings; use strict; use Fcntl qw/ :flock /;
然后我们指定日志的路径和将运行的客户端的数量:
my $log = "/tmp/my.log"; my $clients = 10;
要记录消息,请以附加模式打开文件,以便所有写入都会自动结束.然后调用鸡群等待我们的独家访问日志.一旦我们起床,写信息并关闭手柄,自动释放锁.
sub log_step { my($msg) = @_; open my $fh,">>",$log or die "$0 [$$]: open: $!"; flock $fh,LOCK_EX or die "$0 [$$]: flock: $!"; print $fh "$msg\n" or die "$0 [$$]: write: $!"; close $fh or warn "$0 [$$]: close: $!"; }
现在fork $客户端子进程通过三个步骤之间的随机间隔:
my %kids; my $id = "A"; for (1 .. $clients) { my $pid = fork; die "$0: fork: $!" unless defined $pid; if ($pid) { ++$kids{$pid}; print "$0: forked $pid\n"; } else { my $user = "User" . $id; log_step "$user: Step 1"; sleep rand 3; log_step "$user: Step 2"; sleep rand 3; log_step "$user: Step 3"; exit 0; } ++$id; }
不要忘记等待所有的孩子退出:
print "$0: reaping children...\n"; while (keys %kids) { my $pid = waitpid -1,0; last if $pid == -1; warn "$0: unexpected kid $pid" unless $kids{$pid}; delete $kids{$pid}; } warn "$0: still running: ",join("," => keys %kids),"\n" if keys %kids; print "$0: done!\n",`cat $log`;
样品输出:
[...] ./prog.pl: reaping children... ./prog.pl: done! UserA: Step 1 UserB: Step 1 UserC: Step 1 UserC: Step 2 UserC: Step 3 UserD: Step 1 UserE: Step 1 UserF: Step 1 UserG: Step 1 UserH: Step 1 UserI: Step 1 UserJ: Step 1 UserD: Step 2 UserD: Step 3 UserF: Step 2 UserG: Step 2 UserH: Step 2 UserI: Step 2 UserI: Step 3 UserB: Step 2 UserA: Step 2 UserA: Step 3 UserE: Step 2 UserF: Step 3 UserG: Step 3 UserJ: Step 2 UserJ: Step 3 UserE: Step 3 UserH: Step 3 UserB: Step 3
请记住,订单将与运行不同.