慎用Reactor Notify机制
8 慎用Reactor Notify机制在Reactor的模式,有一种辅助的通知机制,Notify机制,简单说就是通过通知发起者调用notify函数,notify的消息被保存在一个管道中,handle_event的处理中会检查这个管道中是否有通知数据,如果有就根据通知的消息,会根据默认的通知消息的类型去调用hanle_input等函数。 从设计的角度将,这个机制无疑是非常优美的,对于Reactor,它在IO驱动以外,提供了一种新的驱动方式。但是从实现角度来讲,这个机制要慎用。原因有两个。 8.1 ACE Reactor的默认Notify方式采用的是ACE_PipeACE Reactor的默认Notify方式采用的是ACE_Pipe,所以ACE_Pipe在Windows和Linux平台上的问题,Notify机制把ACE_Pipe的缺陷一个不少的继承了,而且问题更加多。 /** * Contains the ACE_HANDLE the ACE_Dev_Poll_Reactor is listening * on,as well as the ACE_HANDLE that threads wanting the attention * of the ACE_Dev_Poll_Reactor will write to. */ ACE_Pipe notification_pipe_; 原来在调试ACE代码的时候,我发现只要一使用Reactor,即使只使用定时器(除非明确不使用Notify),防火墙都会报警有监听端口。我曾经对此大惑不解,直到读了ACE的这部分原代码。这样做的坏处有很多。第一个是由于采用的阻塞IO。速度会慢很多,第二个由于是单线程的处理,如果在压力极大的情况下,可能出现死锁的问题。比如在有大规模的Notify的情况下,发送缓冲区很可能会被塞满(由于是单线程,这时不会有接受者),同时由于为了简化,ACE_Pipe采用的IO是阻塞的,所以会导致整个程序死锁。第三就是这样的情况下ACE_Pipe会打开一个临时的端口,而且会绑定所有的IP(
【注】在一个安全要求严格的环境下,这个临时端口轻则可以让你的服务器轻易陷于崩溃,重则可以让你整个网络被黑客攻陷。
不过还好的是ACE的开发者估计自己也意识倒了这个麻烦。所以提供了另外一种消息队列的方式。你可以通过定义ACE_HAS_REACTOR_NOTIFICATION_QUEUE的宏编译ACE,这样ACE将不使用ACE_Pipe作为Notify消息的管道,而使用一个自己的内存队列保存Notify消息,这个队列是动态扩展的。而且由于是内存操作,性能方面没有太大问题。
大体位置在重复编译的卫哨后面,#include /**/ "ace/pre.h"前面。保证这个宏起到作用。 #ifndef ACE_CONFIG_LINUX_H #define ACE_CONFIG_LINUX_H
//使用内存队列作为Notify Queue #define ACE_HAS_REACTOR_NOTIFICATION_QUEUE
#include /**/ "ace/pre.h"
这个问题到
8.2 考虑不周的Reactor Notify机制同上,这也应该是一个BUG,Reactor Notify的代码有考虑不周的地方。Notify机制的本质是提供了一条消息队列让大家有方法调用Event_handler,但是存在一种可能,在你的通知消息在消息队列的时候,Event_hanlder由于后面的处理可能已经handle_close了。但是ACE的dispatch_notify却没有考虑倒这一点(或者说考虑倒这一点也不好解决)。 ACE_Select_Reactor_Notify::dispatch_notify函数的代码。 int ACE_Select_Reactor_Notify::dispatch_notify (ACE_Notification_Buffer &buffer) { ………… ACE_Event_Handler *event_handler = buffer.eh_;
bool const requires_reference_counting = event_handler->reference_counting_policy ().value () == ACE_Event_Handler::Reference_Counting_Policy::ENABLED; //如果此时这个ACE_Event_Handler已经被handle_close了,你如何是好。。。。 switch (buffer.mask_) { case ACE_Event_Handler::READ_MASK: case ACE_Event_Handler::ACCEPT_MASK: result = event_handler->handle_input (ACE_INVALID_HANDLE); 这个bug到
如果你仔细看过上面的几节,你也许会发出惊叹,啊,又是Reactor Notify?对,又是它。看起来我好像一直在和ACE的Notify机制在做对,但它的确让我吃了无数的苦头。这部分的设计的确有一点画蛇添足的感觉,而且由于跨平台性等原因,这个东东的实现一直不如意。其实自己使用ACE的实现(比如Message_Queue)一套这样的机制应该是易如反掌的事情。不苛求了。 如果你用不到Notify机制,最好在ACE_Reactor初始化的时候彻底关闭Notify机制。很多Reactor的初始化函数都提供了关闭notify pipe的方式。比如ACE_Select_Reactor_T的open函数的disable_notify_pipe参数。当其为1的时候表示关闭notify 管道。 //disable_notify_pipe参数为1时表示关闭NOTIFY PIPE,不使用他 template <class ACE_SELECT_REACTOR_TOKEN> int ACE_Select_Reactor_T<ACE_SELECT_REACTOR_TOKEN>::open (size_t size, int restart, ACE_Sig_Handler *sh, ACE_Timer_Queue *tq, int disable_notify_pipe,/* 等于==1表示关闭notify机制 */ ACE_Reactor_Notify *notify) (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |