機会があったので、以前から少し気になっていた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のソースを追いつつ、絵を描いてたのですが、自分でも微妙な絵になってしまった。。。
とはいえ、一応さらしておきます。

今度もおなじく、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
もうちょっとわかってきたら、アップデートします。
2 Responses
Stay in touch with the conversation, subscribe to the RSS feed for comments on this post.
Continuing the Discussion