加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

制作一个24/7运行并从命名管道读取的Perl守护程序

发布时间:2020-12-15 21:54:55 所属栏目:大数据 来源:网络整理
导读:我正在尝试使用perl制作日志分析器.分析器将在AIX服务器的后台运行24/7,并从syslog将日志定向到的管道(从整个网络)读取.基本上: logs from network ---- named pipe A -------- | perl daemon ---- named pipe B -------- | * reads pipes ---- named pipe
我正在尝试使用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

因此,例如,我希望我的守护进程能够配置为将所有写入命名管道C的日志发送到root@domain.com.为此,我假设守护进程需要有一个哈希值(perl的新内容),但这似乎是一个适当的数据结构),可以在运行中进行更改,并告诉它如何处理每个管道.

这可能吗?或者我应该在/ etc中创建一个.conf文件来保存信息.像这样的东西:

namedpipeA:'mail root@domain.com'
namedpipeB:save:'mail user@domain.com'

因此,从A获取任何内容将邮寄到root@domain.com,B中的所有内容都将保存到日志文件中(通常是这样)并将其发送到user@domain.com

看到这是我第一次使用Perl和我第一次创建一个守护进程,无论如何,我是否仍然坚持KISS校长?还有,我应该遵守哪些惯例?如果您在回复时可以考虑到我缺乏知识,那将是最有帮助的.

解决方法

我将介绍您的部分问题:如何编写一个处理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;

然后,我们将创建一个时间观察者,或“计时器”:

my $t = AnyEvent->timer( after => 0,interval => 5,cb => sub {
    say "Hello";
});

请注意,我们将计时器分配给变量.只要$t在范围内,这就可以使计时器保持活动状态.如果我们说undef $t,那么定时器将被取消并且永远不会调用回调.

关于回调,这是cb =>之后的子{…},这就是我们处理事件的方式.发生事件时,将调用回调.我们做了我们的事情,返回,并且事件循环继续根据需要调用其他回调.您可以在回调中执行任何操作,包括取消和创建其他观察者.只是不要进行阻止调用,例如system(“/ bin / sh long running process”)或my $line =< $fh>或睡觉10.任何阻挡必须由观察者完成;否则,在等待该任务完成时,事件循环将无法运行其他处理程序.

现在我们有了一个计时器,我们只需要进入事件循环.通常,您将选择要使用的事件循环,并以事件循环文档描述的特定方式输入它. EV是一个很好的,你通过调用EV :: loop()输入它.但是,我们将让AnyEvent通过编写AnyEvent-> condvar-> recv来决定使用什么事件循环.不要担心这样做;这是一个成语,意思是“进入事件循环,永不回归”. (当你读到AnyEvent时,你会看到很多关于条件变量或condvars的东西.它们对文档和单元测试中的例子都很好,但你真的不想在程序中使用它们.如果你在.pm文件中使用它们,你做的事情非常错误.所以只是假装它们现在不存在,你会从一开始就编写非常干净的代码.这会让你领先于很多CPAN作者!)

所以,只是为了完整性:

AnyEvent->condvar->recv;

如果你运行该程序,它将每隔五秒打印一次“Hello”,直到宇宙结束,或者更可能的是,你用控制c杀死它.这有什么用的,你可以在打印“Hello”之间的那五秒内做其他事情,而你只需添加更多的观察者即可.

所以,现在从管道读取. AnyEvent使用AnyEvent :: Handle模块使这很容易. AnyEvent :: Handle可以连接到套接字或管道,只要有数据可供读取,它就会调用回调. (它也可以执行非阻塞写入,TLS和其他东西.但我们现在不关心它.)

首先,我们需要打开一个管道:

use autodie 'open';
open my $fh,'<','/path/to/pipe';

然后,我们用AnyEvent :: Handle包装它.创建Handle对象后,我们将它用于此管道上的所有操作.你可以完全忘记$fh,AnyEvent :: Handle会直接触摸它.

my $h = AnyEvent::Handle->new( fh => $fh );

现在我们可以使用$h来读取管道中可用的行:

$h->push_read( line => sub {
    my ($h,$line,$eol) = @_;
    say "Got a line: $line";
});

当下一行可用时,这将调用打印“获得一条线”的回调.如果要继续读取行,则需要使函数将自身推回到读取队列,如:

my $handle_line; $handle_line = sub {
    my ($h,$eol) = @_;
    say "Got a line: $line";
    $h->push_read( line => $handle_line );
};
$h->push_read( line => $handle_line );

这将读取行并为每行调用$handle_line->(),直到文件关闭.如果你想提前停止阅读,这很容易……在这种情况下再也不要再推了一下. (您不必在行级读取;您可以要求在任何字节可用时调用您的回调.但这更复杂并留给读者练习.)

所以现在我们可以将它们组合成一个处理读取管道的守护进程.我们想要做的是:为行创建一个处理程序,打开管道并处理这些行,最后设置一个信号处理程序来干净地退出程序.我建议采用OO方法来解决这个问题;使每个动作(“处理来自访问日志文件的行”)具有启动和停止方法的类,实例化一堆动作,设置信号处理程序以干净地停止动作,启动所有动作,然后进入事件循环.这是很多与这个问题没有关系的代码,所以我们会做一些更简单的事情.但在设计程序时请记住这一点.

#!/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,$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;

现在你有一个程序从任意数量的管道中读取一行,打印在任何管道上接收的每一行(前缀为管道的路径),并在按下Control-C时完全退出!

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读