Skip to content


EventMachineを触ってみた

機会があったので、以前から少し気になっていたEventMachineを触ってみました。EventMachineがどんなものかというのは下記のスライドに簡単にまとまっています。(いつもお世話になってるWebサービスで結構使用されてます)

EventMachine: scalable non-blocking i/o in ruby

Githubにあるeventmachineのtutorialにあるリンクをテキトーに見てみました。

まずは、一番簡単な例
An EventMachine Tutorial”

require "rubygems"
require "eventmachine"

# From "An EventMachine Tutorial"
# http://20bits.com/articles/an-eventmachine-tutorial/

# ハンドラー
module EchoServer
  # connectionからデータを受け取った時に呼び出されるメソッド
  def receive_data(data)
    # receive_dataを呼び出したconnectionにデータを返す
    # receive_dataしたらsend_dataする
    send_data(data)
  end
end

# イベントループ開始
# EventMachine::stop_event_loopを呼び出すまで止まらない
EventMachine::run do
  host = '0.0.0.0'
  port = 8080

  # 3番目の引数はHandlerで、コールバック用の関数でグローバル変数を汚染しないために
  # moduleを定義することが多い。
  EventMachine::start_server host,port,EchoServer
  puts "started EchoServer on #{host}:#{port}"
end

このスクリプトを実行させておいて、”telnet 0.0.0.0 8080″で繋げれば、入力したものがそのまま返されます。

おなじく、An EventMachine Tutorial”
にあるサーバーの例

require "rubygems"
require "eventmachine"

# From "An EventMachine Tutorial"
# http://20bits.com/articles/an-eventmachine-tutorial/

class Server < EventMachine::Connection
  attr_accessor :status,:options

  def receive_data
    puts "#{@status} -- #{data}"
    send_data("hello\n")
  end
end

EventMachine::run do
  # start_serverは3番目の引数に指定されたハンドラーを引数にyeildする
  EM::start_server host,port,Server do |conn|
    conn.options = {:my => 'options'}
    conn.status = :OK
  end
end

この例では、Serverクラスをハンドラーに指定しています。EventMachine::start_serverは任意のモジュールかクラスをハンドラーに指定することができます。モジュールを指定した場合もEventMachine::Connectionクラスにそのモジュールがインクルードされた後、接続したクライアントごとにインスタンス化されます。

そこらへんはEventMachineのソースを追いつつ、絵を描いてたのですが、自分でも微妙な絵になってしまった。。。
とはいえ、一応さらしておきます。
EventMachine

今度もおなじく、An EventMachine Tutorial”ですが、httpクライアントの例

require "rubygems"
require "eventmachine"

# From "An EventMachine Tutorial"
# http://20bits.com/articles/an-eventmachine-tutorial/

module HttpHeaders
  # connectionがセットアップされた直後に呼び出される。
  # クライアントアプリならサーバーにつながった直後/サーバーアプリならクライアントが接続してきた直後
  def post_init
    send_data "GET /\r\n\r\n"
    @data = ""
  end

  def receive_data(data)
    @data << data
  end

  # クライアント/サーバー側のいずれかの接続が終了した時
  # このhttp_clientの場合は、サーバーがデータを送り終えたら呼び出される
  def unbind
    puts "server have sent data"
    if @data =~ /[\n][\r]*[\n]/m
      $`.each do |line|
        puts ">>> #{line}"
      end
    end

    # ループを終了させる
    EventMachine::stop_event_loop
  end
end

EventMachine::run do
  EventMachine::connect "google.com",80,HttpHeaders
end

ポイントとしては、

  • 接続の開始時にpost_init/終了時にunbindが実行される
  • TCPはストリームなので、receive_dataは何度も実行され、渡されるデータの単位は(改行区切りとか)意味のあるものではない
  • というところでしょうか。

    今度は、Playing with EventMachineにあるタイマーの設定例

    require "rubygems"
    require "eventmachine"
    
    # Playing with EventMachine
    # http://everburning.com/news/playing-with-eventmachine/
    
    # EventMachineとEMは同じ
    # runでイベントループが開始されるが、引数として渡されるブロックをその前に実行する
    EventMachine::run do
      # 定期的に実行されるタイマーを設定
      EM.add_periodic_timer(1) { puts "Tick ..." }
    
      # 指定した秒数が経過した後に、1回実行されるタイマーを設定
      EM.add_timer(3) do
        puts "I waited 3 seconds"
        EM.stop_event_loop
      end
    end
    
    puts "All done"
    

    一応試してみたコードはGithubにアップしてます。em_snipett
    もうちょっとわかってきたら、アップデートします。

Posted in Programming. Tagged with , .

2 Responses

Stay in touch with the conversation, subscribe to the RSS feed for comments on this post.

Continuing the Discussion

  1. Tweets that mention EventMachineを触ってみた « Stranger in Paradigm -- Topsy.com linked to this post on May 16, 2010

    [...] This post was mentioned on Twitter by t4ku d2s4. t4ku d2s4 said: EventMachineを触ってみた http://bit.ly/crLTQ7 [...]

  2. Alexander7 linked to this post on July 22, 2011

    buy@generic.LEVITRA” rel=”nofollow”>…

    Need cheap generic LEVITRA?…

Some HTML is OK

(required)

(required, but never shared)

or, reply to this post via trackback.