n0p*_*0pe 7 perl daemon syslog named-pipes
我正在尝试使用perl制作日志分析器.分析器将在AIX服务器的后台运行24/7,并从syslog将日志定向到的管道(从整个网络)读取.基本上:
logs from network ----> named pipe A --------> | perl daemon
----> named pipe B --------> | * reads pipes
----> named pipe c --------> | * decides what to do based on which pipe
Run Code Online (Sandbox Code Playgroud)
因此,例如,我希望我的守护程序能够配置为mail root@domain.com写入的所有日志named pipe C.为此,我假设守护进程需要有一个哈希(perl的新内容,但这似乎是一个合适的数据结构),它可以在运行中进行更改并告诉它如何处理每个管道.
这可能吗?或者我应该创建一个.conf文件/etc来保存信息.像这样的东西:
namedpipeA:'mail root@domain.com'
namedpipeB:save:'mail user@domain.com'
Run Code Online (Sandbox Code Playgroud)
所以从中获取任何东西A将被邮寄到,root@domain.com并且所有内容都B将保存到日志文件中(通常是这样)并且它将被发送到user@domain.com
看到这是我第一次使用Perl和我第一次创建一个守护进程,我是否仍然坚持KISS校长这样做?还有,我应该遵守哪些惯例?如果您在回复时可以考虑到我缺乏知识,那将是最有帮助的.
jro*_*way 18
我将介绍你的部分问题:如何编写一个处理IO的长期运行的Perl程序.
编写处理许多同时IO操作的Perl程序的最有效方法是使用事件循环.这将允许我们为事件编写处理程序,例如"命名管道上出现一行"或"电子邮件已成功发送"或"我们收到SIGINT".至关重要的是,它允许我们在一个程序中组成任意数量的这些事件处理程序.这意味着您可以"多任务"但仍然可以轻松地在任务之间共享状态.
我们将使用AnyEvent框架.它允许我们编写事件处理程序,称为观察程序,它将与Perl支持的任何事件循环一起使用.您可能不关心使用哪个事件循环,因此这种抽象可能与您的应用程序无关.但它将让我们重用CPAN上可用的预先编写的事件处理程序; AnyEvent :: SMTP用于处理电子邮件,AnyEvent :: Subprocess用于与子进程交互,AnyEvent :: Handle用于处理管道,等等.
基于AnyEvent的守护进程的基本结构非常简单.你创建一些观察者,进入事件循环,然后......就是这样; 事件系统完成其他所有事情.首先,让我们编写一个程序,每五秒打印一次"Hello".
我们首先加载模块:
use strict;
use warnings;
use 5.010;
use AnyEvent;
Run Code Online (Sandbox Code Playgroud)
然后,我们将创建一个时间观察器或"计时器":
my $t = AnyEvent->timer( after => 0, interval => 5, cb => sub {
say "Hello";
});
Run Code Online (Sandbox Code Playgroud)
请注意,我们将计时器分配给变量.只要$t在范围内,这就使计时器保持活动状态.如果我们说undef $t,那么计时器将被取消,并且永远不会调用回调.
关于回调,这是sub { ... }后cb =>,这就是我们处理事件的方式.发生事件时,将调用回调.我们做了我们的事情,返回,并且事件循环继续根据需要调用其他回调.您可以在回调中执行任何操作,包括取消和创建其他观察者.只是不要拨打阻止电话,system("/bin/sh long running process")或者my $line = <$fh>或者sleep 10.阻挡的任何东西都必须由观察者完成; 否则,在等待该任务完成时,事件循环将无法运行其他处理程序.
现在我们有了一个计时器,我们只需要进入事件循环.通常,您将选择要使用的事件循环,并以事件循环文档描述的特定方式输入它. EV是一个很好的,你通过电话输入它EV::loop().但是,我们将让AnyEvent通过编写来决定使用什么事件循环AnyEvent->condvar->recv.不要担心这样做; 这是一个成语,意思是"进入事件循环,永不回归".(当你阅读关于AnyEvent的内容时,你会看到很多关于条件变量或condvars的内容.它们对于文档和单元测试中的示例很有用,但你真的不希望在你的程序中使用它们.如果你"在.pm文件中使用它们,你做的事情非常错误.所以只是假装它们现在不存在,并且你从一开始就编写非常干净的代码.这将使你领先于许多CPAN作者!)
所以,只是为了完整性:
AnyEvent->condvar->recv;
Run Code Online (Sandbox Code Playgroud)
如果你运行该程序,它将每隔五秒打印一次"Hello",直到宇宙结束,或者更可能的是,你用控制c杀死它.这个问题很简单,你可以在打印"Hello"之间的五秒钟内做其他事情,而你只需添加更多的观察者即可.
所以,现在从管道读取.AnyEvent使用AnyEvent :: Handle模块使这很容易.AnyEvent :: Handle可以连接到套接字或管道,只要有数据可供读取,它就会调用回调.(它也可以执行非阻塞写入,TLS和其他东西.但我们现在不关心它.)
首先,我们需要打开一个管道:
use autodie 'open';
open my $fh, '<', '/path/to/pipe';
Run Code Online (Sandbox Code Playgroud)
然后,我们用AnyEvent :: Handle包装它.创建Handle对象后,我们将它用于此管道上的所有操作.你可以完全忘记$fh,AnyEvent :: Handle将直接处理它.
my $h = AnyEvent::Handle->new( fh => $fh );
Run Code Online (Sandbox Code Playgroud)
现在我们可以用来$h从管道中读取线条:
$h->push_read( line => sub {
my ($h, $line, $eol) = @_;
say "Got a line: $line";
});
Run Code Online (Sandbox Code Playgroud)
当下一行可用时,这将调用打印"获得一行"的回调.如果要继续读取行,则需要使函数将自身推回到读取队列,如:
my $handle_line; $handle_line = sub {
my ($h, $line, $eol) = @_;
say "Got a line: $line";
$h->push_read( line => $handle_line );
};
$h->push_read( line => $handle_line );
Run Code Online (Sandbox Code Playgroud)
这将读取行并调用$handle_line->()每一行,直到文件关闭.如果你想提前停止阅读,这很容易...... push_read在这种情况下再也不要了.(您不必在行级读取;您可以要求在任何字节可用时调用您的回调.但这更复杂,并留给读者练习.)
所以现在我们可以将它们组合成一个处理读取管道的守护进程.我们想要做的是:为行创建一个处理程序,打开管道并处理这些行,最后设置一个信号处理程序来干净地退出程序.我建议采用OO方法来解决这个问题; 使每个动作("从访问日志文件处理行")使用start和stop方法的类,实例化一堆动作,设置信号处理程序以干净地停止动作,启动所有动作,然后进入事件循环.这是很多与这个问题没有关系的代码,所以我们会做一些更简单的事情.但在设计程序时请记住这一点.
#!/usr/bin/env perl
use strict;
use warnings;
use AnyEvent;
use AnyEvent::Handle;
use EV;
use autodie 'open';
use 5.010;
my @handles;
my $abort; $abort = AnyEvent->signal( signal => 'INT', cb => sub {
say "Exiting.";
$_->destroy for @handles;
undef $abort;
# all watchers destroyed, event loop will return
});
my $handler; $handler = sub {
my ($h, $line, $eol) = @_;
my $name = $h->{name};
say "$name: $line";
$h->push_read( line => $handler );
};
for my $file (@ARGV) {
open my $fh, '<', $file;
my $h = AnyEvent::Handle->new( fh => $fh );
$h->{name} = $file;
$h->push_read( line => $handler );
}
EV::loop;
Run Code Online (Sandbox Code Playgroud)
现在你有一个程序从任意数量的管道中读取一行,打印在任何管道上接收的每一行(前缀为管道的路径),并在按下Control-C时完全退出!