l'essentiel est invisible pour les yeux

Wednesday, May 16, 2007

[Ruby] Ruby/EventMachineでネットワークプログラミング

Ruby/EventMachine

Ruby/EventMachineはシングルスレッドで高速に動作するネットワークプログラミングのためのライブラリでIPレイヤを扱うコア部分はC++で実装されている。(Pure Ruby版も用意されている。)

イベントドリブンなアーキテクチャで実装されており、ユーザはIP層を全くほとんど意識することなくプロトコルの実装に集中できるように設計されている。ユーザが書くソースコードでは決まったクラスを継承し決まったメソッドをオーバーライトするだけでよい。

現在の実装は、Unix系のシステムを使用している場合はslect(2)システムコールで実装されている。将来のバージョンではepoll(4)に置換したいとのこと。

文字数を返すサーバ


require 'rubygems'
require 'eventmachine'

module CharacterCount < EventMachine::Connection
def post_init
puts "Received a new connection"
@data = ""
@line_count = 0
end

def receive_data(data)
puts "Received data: #{data}"
send_data "#{data.length} (characters)\r\n"
close_connection_after_writing
end
end

EventMachine::run do
host, port = "127.0.0.1", 3793
EventMachine.start_server(host, port, CharacterCount)
puts "Now accepting connections on address #{host}, port #{port}"
EventMachine.add_periodic_timer(10) {$stderr.write '*'}
end



EventMachine::Connection#post_initがクライアントからの接続を受け付けた時に呼び出され、受信したデータはEventMachine::Connection#received_dataの引数に渡される。post_initはEventMachine内のループでリクエストを受け付けるたびにクラスがインスタンス化され呼び出される。ユーザのコード内でこのクラスをインスタンス化することは決してない。

日付を返すメソッド(get_date)を定義したサーバ。

require 'rubygems'
require 'eventmachine'

# This is simple protocol which get date.
class GetDateProtocol < EventMachine::Connection
def receive_data(data)
puts "Received data: #{data}"
if pattern = data.split("get_date: ")[1]
send_data Time.now.strftime(pattern)
elsif data.index("exit") == 0
close_connection_after_writing
else
send_data "ERROR: This method isn't implemented.\r\n"
end
end

def unbind
puts "Connection is closed."
end
end

EventMachine::run do
host, port = "127.0.0.1", 3793
EventMachine.start_server(host, port, GetDateProtocol)
puts "Now accepting connections on address #{host}, port #{port}"
end

unbindは接続が切断された時に呼び出される。その他のメソッドについてはeventmachin.rbを参照。

ネットワーク部分の大部分がC++で書かれており高速に動作しかつ、プロトコルの実装に集中することができる。Ruby/EventMachineのドキュメント中ではC10K問題がライブラリを作るきっかけとなったことについて触れられているが、大本命はやはりErlangかな。