EventMachine: Reactor in Ruby

阅读更多

    前段时间用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,以获取更好的效率,避免了前者的局限性(存在句柄数量的限制、大句柄集合效率低下)

你可能感兴趣的:(Ruby,rubygems,Mina,网络应用,Python)