前段时间用C++实现过Reactor模式,今天看到InfoQ上对 EventMachine 的介绍,原来也是基于该模式的一个框架,很想借鉴一下成熟作品的实现方式。Google了一下 Reactor Pattern,原来已经有很多成熟作品 了:
The ADAPTIVE Communication Environment (C++)
Apache MINA (Java)
POE (Perl)
Twisted (Python)
EventMachine (Ruby)
使用EventMachine编写网络应用相当简洁,下面是来自EM文档的两个例子:
Server.rb
require 'rubygems' require 'eventmachine' module EchoServer def receive_data data send_data ">>>you sent: #{data}" close_connection if data =~ /quit/i end end EventMachine::run { EventMachine::start_server "192.168.0.100", 8081, EchoServer }
Client.rb
require 'rubygems' require 'eventmachine' module HttpHeaders def post_init send_data "GET /\r\n\r\n" @data = "" end def receive_data(data) @data << data end def unbind if @data =~ /[\n][\r]*[\n]/m $`.each {|line| puts ">>> #{line}" } end EventMachine::stop_event_loop end end EventMachine::run do EventMachine::connect 'microsoft.com, 80, HttpHeaders end
对于用户而言,我们关心的是如何向Reactor注册我们关注的事件和相应的处理函数,然而上面的例子中根本就没有相应的代码出现,这就显得有点怪异了,因为我们使用Reactor时,通常都会有构建一个与应用相关的Handler,然后将其注册给Reactor的过程。
其实,我们的直觉是对的,只是EM把这个过程封装起来而已。 EM把Handler抽象为类Connection,里面定义了模板方法,我们可以通过继承该类并覆盖相应的模板方法以实现自己的应用逻辑。
class HttpHeaders < EventMachine::Connection def initialize *args super # whatever else you want to do here end #.......your other class code end # class HttpHeaders EventMachine::run do EventMachine::connect 'microsoft.com, 80, HttpHeaders end
该代码跟上面的Client.rb是完全等价的,EM会自动判断该代码是Class还是Module,如果是后者,则将其mixin进class Connection中。
在实现上,EM没有使用太多花哨的技巧,同时为了提高性能,它的核心组件Reactor使用C++来实现。下面介绍一下几个重要的方法。
1、EventMachine::run
initialize_event_machine #初始化
add_timer 0, block # block 就是传递进来的 EventMachine::connect
run_machine #启动循环
2、EventMachine::connect
#判断传递进来的handler是Class还是Module,如果是后者,则将其mixin进class Connection
klass = if (handler and handler.is_a?(Class))
handler
else
Class.new( Connection ) {handler and include handler}
end
....
s = connect_server server, port #取得TCP连接,获取句柄
c = klass.new s, *args #构建Connection实例
3、Connection覆盖了操作符 new
associate_callback_target sig #在Reactor中注册
post_init
用户通过定义module或者继承Connection后,EM则通过上述方法自动完成事件注册和分派工作,用户只需专心完成应用逻辑即可。此外,EM在Linux2.6环境下,则自动选用epoll来代替select,以获取更好的效率,避免了前者的局限性(存在句柄数量的限制、大句柄集合效率低下)