您的位置:首页 > 职场人生

事件框架 POE 学习

2011-07-07 09:36 260 查看
转载本站文章请注明,转载自:扶凯[http://www.php-oa.com]

本文链接: http://www.php-oa.com/2009/09/09/perl-poe-1.html

POE结构:

Kernel

|

Session

| _______________________________ |

| | |

Driver->Filter->Wheel->Components Kernel:

POE管理会话的内核Kernel,它是POE核心,内部实现了IO读写信号回调等处理,简单应用程序与Kernel交互并不多。 Session: 会话抽象,会话中需要创建高层协议抽象,Session是一个处理线程,比如一个服务器程序每一个客户端连接就应该对应于一个Session,同理 Spider程序中对于每一个web服务器的连接也应该对应于一个Session,一个应用程序可以有很多Session。

Driver: 底层文件操作的抽象,在编程时不会直接用到,Wheel、Filter、Driver是对底层IO的封装。

Filter: 底层、中层协议操作的抽象,通常不会直接用到

Wheel: 高层协议操作的抽象,经常要用到

Components:POE提供的一些拿来就能用的组件

POE对象的数据结构:

$_[HEAP]:是会话唯一的数据存储区,相当有用的一个东西,它就是一个容器,相当于普通的Perl中的全局变量.这个在整个回话中,就用它 来通信;

$_[SESSION]:是指向会话自身的引用;

$_[KERNEL]:是指向会话管理内核的引用;

@_[ARG0..ARG9]:用于传递给各事件处理函数的参数,这是调用在传送参数给别的事件时,除开存在heap中的信息外的内容.都放在 这;

my ($kernel, $session, $heap) = @_[ KERNEL, SESSION, HEAP ];
my ($currentItem,$count) = @_[ ARG0..$#_ ];


如上,这是事件调用后,比较建议给通用的信息放到第一行.第二行是其它参数接收方法,这个中 @_[ ARG0..$#_ ],很象普通的子函数调用@_一样.

POE中几种事件调用的方法

yield:将一个事件放入当前会话fifo分派队列的末尾处,当队列中在其之前的事件被处理完毕之后,该事件将被触发以运行相应的事件句柄。

call : 是立即执行该事件.

post: 放到事件队列的最后,这个和call基本一样,但可以指定事件

delay: 从现在到设置的时间后,才运行事件. delay 可以加多个参数,它后面接的第二个参数为delay的时间,第三个参数为第一个调用事件的参数. delay后面只加事件名,不加参数的话,就是消掉这个事件,不在运行.

postback: 给事件放到后台,调用后才会还回

delay_set: 和上面一样,几秒后运行,我还没有搞清它和 delay 的分别.

POE 的一些需要了解的东西

句柄:它就是你给一个文件,设备,管套(socket)或管道的一个名字, 以便帮助你记住你正处理的名字, 并隐藏某些缓存等的复杂性。"

事件; 事件就是真实世界中发生的一件事情。比如说:早晨打铃、面包从烤机里弹出、茶煮好了等。而在用户界面 上最常见的事件则是鼠标移动、按钮点击和键盘敲打等等。具体到程序软件事件,则往往是一些抽象的事件。也就是说,它不仅包括了发送给程序的外部活动,而且 也包括了一些在操作系统内部运行的事件。比如说,计时器到点了,socket建立了连接,下载完成等。

事件句柄: 中心分配器的作用是将事件分配给不同的处理程序。这些处理程序就是事件句柄.它们的任务就是处理 相应事件,事件句柄就是Perl程序。它们因为使用了POE::Kernel传递的参数而不同于一般的perl程序。

POE::Session: 就是由POE::Kernel管理的“任务”。每一个session还拥有自己 的资源和事件句柄。

heap: 每一个session都有一个自己私有的存储空间,叫“heap”。它用于定义会话的heap, 传递给$_[HEAP],默认情况是匿名空Hash。这个就是上面指到的数据结构中的$_[HEAP]部分.记的,这个就象个全局引用,给需要的数据都放 到这个中.

POE::Kernel: 它提供了基于事件的操作系统核心服务。包括I/O事件、警报和其它计时事件、信号 事件和一些不被人意识到的事件。POE::Kernel提供不同的方法对这些事件进行设置,比如select_read(), delay()和sig()。POE::Kernel还能够跟踪事件发生源和与之相关的任务之间的关系。之所以能够这么做,是因为当事件发生时,它将跟踪 哪个任务被激活了。于是它便知道了哪个任务调用方法来使用了这些资源,而这些都是自动完成的。POE::Kernel也知道何事需将任务销毁。它检测任务 以确定是否还有事件需要处理,或者是哪个事需要释放占用的资源。当任务没有事件可以触发的时候,POE::Kernel就自动销毁该资源。

参数: POE::Kernel是通过@_来传递参数的.也就是上面数据结构中提到的"@_[ KERNEL, SESSION, HEAP ] 和 @_[ARG0..ARG9]",前三个成员定义了发生该事件的session的上下文。 KERNEL 指向POE::Kernel运行实例的引用,指事件自身的名字. SESSION 指向发出事件的session的引用. HEAP 指向私有heap的引用 @_中剩下的成员属于事件自身,其中的具体内容依照被指派的事件类型而定。 举例说明:对于I/O事件,包括两个参数:一个是缓冲文件句柄,另一个是用来说明采取何种行为(input、output或者异常)的标记。POE还允许 改变参数的顺序和数量,而不会对程序造成影响。比如说,KERNEL,HEAP和ARG0分别是POE::Kernel实例、当前session的堆栈和 事件的第一个用户参数。它是传递给_start状态,即_start事件参数(ARG0..$#_)。 一次性以队列片段的形式赋值给程序参数。

my ($kernel, $session, $heap) = @_[ KERNEL, SESSION, HEAP ];
my ($currentItem,$count) = @_[ ARG0..$#_ ];


实例分析

现在我们来拿Flw大大的程序来分析POE的工作原理. 注意,在这个程序基本上可以分为三部分

1) POE::Session->create,创建一个Session。

2) POE::Kernel->run, 启动框架消息分发。

3) sub start … 定义各种状态的回调函数。

use POE;

create POE::Session(
inline_states =>
{
_start => \&session_start,
_stop  => \&session_stop,
count  => \&session_count,
}
);

print "启动 POE 内核...\n";
POE::Kernel->run();
print "POE 内核运行结束。\n";
exit;

sub session_start {
print "Session 启动。Session ID = ", $_[SESSION]->ID, "\n";
$_[HEAP]->{count} = 0;
$_[KERNEL]->yield("count");
}

sub session_stop {
print "Session 停止。Session ID = ", $_[SESSION]->ID, ".\n";
}

sub session_count {
my ( $kernel, $heap ) = @_[ KERNEL, HEAP ];
my $session_id = $_[SESSION]->ID;

$heap->{count}++;
printf "计数器 = %s\n", $heap->{count};

if ( $heap->{count} < 5 ) {
$kernel->yield("count");
}
}


use POE; #加载了一些诸如POE::Kernel和POE:Sesson的模块并相应地做了一些初始化,并由POE::Session会根据不同的事件输出默认常 量给事件句柄的某些参数,如:KERNEL,HEAP,ARG0等等。

1.建立session 接着建立至少一个session。程序最少要有事情可以做.不然POE::Kernel会在最后一个session停止以后终止运行。

create POE::Session(
inline_states =>
{
_start => \&session_start,
_stop  => \&session_stop,
count  => \&session_count,
}
);


其中的inline_states(HASHREF)表示将事件名映射到处理该事件的代码引用。还有一个 object_states(LISTREF)将事件名映射到处理它们的对象方法,值是对象引用和对象方法引用组成的列表的引用。 两个事件(最上面提到的事件句柄)是由POE::Kernel自身所提供的。它们分别表示该session的启动(_start)和销毁(_stop)。 最后一个事件(count)是用户自定义事件,它被用于程序的逻辑之中。下面我们会来亲自写这几个函数.

2.启动POE::Kernel 由此便建立了一个用来探测并分派事件的主循环。我们使用print来打印POE::Kernel运行的开始处和结束点。

print "启动 POE 内核...\n";
POE::Kernel->run();
print "POE 内核运行结束。\n";
exit;


Kernel的run方法只有在所有session返回之后才会停止循环。

3.由上面定义的3个事件句柄:

_start句柄 在上面建立session的时候.首先从_start开始。_start的句柄将在sesson初始化完成之后开始运行,session在其自身的上下文 中使用它来实现输入引导。比如初始化heap中的值,或者分配一些必要的资源等等。 在该句柄中我们建立了一个累加器,并且发出了“count”事件以触发相应的事件句柄。

sub session_start {
print "Session 启动。Session ID = ", $_[SESSION]->ID, "\n";
$_[HEAP]->{count} = 0;
$_[KERNEL]->yield("count");
}


在这,我们使用yield方法,将一个事件放入fifo分派队列的末尾处,当队列中在其之前的事件被处理完毕之后,该事件将被触发以运行相应的事件 句柄。

_stop句柄。 POE::Kernel将在所有session再无事件可触发之后,并且是在自身被销毁之前调用它。

sub session_stop {
print "Session 停止。Session ID = ", $_[SESSION]->ID, ".\n";
}


count事件句柄。 该函数用来增加heap中的累加器计数,并打印累加结果。为了加深对yield的印象,我们现在使用yield方法来调用新的事件,这样也能够加深对 POE事件处理原理的理解。

sub session_count {
my ( $kernel, $heap ) = @_[ KERNEL, HEAP ];
my $session_id = $_[SESSION]->ID;

$heap->{count}++;
printf "计数器 = %s\n", $heap->{count};

if ( $heap->{count} < 5 ) {
$kernel->yield("count");
}
}


POE输出:只要累加器计数未超过10,session将再yield一个count事件。因为不断地触发了 session_count句柄,使得当前session可以继续得以生存而不会被POE::Kernel清理。 当计数器到10时,便不再调用yield命令,session也将停止。一旦POE::Kernel检测到该session再没有事件句柄可被激发,便在 调用_stop事件句柄之后将其清理销毁。 以下是运行的结果:

Session 启动。Session ID = 2
启动 POE 内核...
计数器 = 1
计数器 = 2
计数器 = 3
计数器 = 4
计数器 = 5
Session 停止。Session ID = 2.
POE 内核运行结束。


有没有注意到,第一个count时本来是要打印0的,但是直接打印1.为什么,主要是因为下面,yield是需要事件完后才调用.

POE 其它注意

session->get_heap() 通过 session 来得到 heap.

常用POE的组件

POE::API::Peek 这是一个可以查看 POE 核心事件的接口

POE::Wheel::Run 制造轮子,常用来做并发的事件

POE::Filter::Line 行过滤器

POE::Wheel::SocketFactory 非阻塞套接字创建

POE::Component::Server::TCP 一个简单的TCP服务器

POE::Wheel::ListenAccept
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  职场 休闲 poe