おさらい
第29回では,非同期処理を実現する方法としてReactorパターンを紹介しました。
一般的に非同期処理として使われているスレッド処理と違い,Reactorパターンはシングルスレッドで動作し,スレッド間のデータの共有に悩む必要はありません。しかしReactorパターンはコードが直感的ではなく,慣れていない人には取っつきにくいものであることも確かです。
そのため,Reactorパターンを意識せずに非同期処理を実現するためのライブラリがRubyにはたくさん用意されています。
今回は,その中でもよく使われているEventMachineについて簡単に説明します。
EventMachineとは
EventMachineとは,Reactorパターンを利用して非同期処理を実現し,高レベルなネットワークインターフェースを提供するライブラリです。
複雑なReactorパターンによる処理はブラックボックス化されているため,開発者は自分の書きたいコードに集中することができます。
また,高速に動作することから,WebサーバーのThinなどにも利用されています。
EventMachineのインストールと作法
EventMachineは,gemを使って簡単にインストールすることができます。
では,前回のテストコードと同じように,複数のwebサーバからHTML文章を取得してみましょう。
一般的に,EventMachineで何か処理をする場合は,moduleまたはEventMachine::Connectionを継承したクラスにコールバックを実装し,それをEventMachine.connectに渡します。
この方法でもよいのですが,EventMachineにはHTTPのクライアント処理に適応したクラス,EventMachine::Protocols::HttpClientが用意されているので,それを利用してみましょう。
EventMachineでクライアント処理
require 'rubygems'
require 'eventmachine'
hosts = [ホスト1, ホスト2, ...]
hosts_size = hosts.size
EventMachine.run do
hosts.each do |h|
http = EventMachine::Protocols::HttpClient.request(:host => h, :port => 80, :request => "/")
http.callback do |response|
EventMachine.stop_event_loop if (hosts_size -= 1) <= 0
end
end
end
まずはrequestクラスメソッドでそれぞれのサーバに対してリクエストを送信します。前回のサンプルコードではリクエストを送信する際も IO.selectメソッドで待っていましたが,Eventmachineは同様の処理をrequestメソッドで行ってくれます。
レスポンスが返ってきたときはcallbackメソッドに渡したブロックが実行されます。ブロック引数のresponseは,HTTPステータスコード,レスポンスヘッダ,返されたHTMLを持っています。
すべてのサーバからのレスポンスを受信したのち,EventMachine.stop_event_loopでイベントループを終了します。
いかがでしょうか。Reactorパターンを意識する必要はほとんどないことが理解できるかと思います。プログラマは実際にやりたいことを,callbackに渡すブロック内で実現すればいいのです。
なお,上記のコードはあくまでサンプルなのでエラー処理などは行っていませんが,エラー発生時に実行する処理はerrbackメソッドで定義することができます。
さまざまなプロトコル
EventMachineはIOの待ちが発生するところならば,基本的にどこでも使えます。そのため,SMTPやmemcached,またFileI/OやPostgreSQLのクライアント処理に適応したクラスが用意されています。
利用したいプロトコルが用意されていなかったとしても心配はいりません。コネクション処理の実装であるEventMachine::Connection クラスや,コールバック系のメソッド群であるEventMachine::Deferrableモジュールなどを利用すれば,自分の欲しい機能を実装することが可能でしょう。
サーバ処理
EventMachineはほとんどサーバ側の処理に使われているようです。Web上にも,そのようなコードをよく見かけます(echoサーバの例)。
豊富なサンプルもあることですので,本稿ではサーバ側の処理については特に説明はしません。
moduleまたはEventMachine::Connectionを継承したクラスにコールバックを実装し,それをEventMachine.connectに渡すという点では,クライアント側の処理の実装と大きく変わる部分はないのです。
まとめ
Reactorパターンについて,そしてReactorパターンを利用したライブラリEventMachineについて簡単ですが説明してきました。
非同期処理を実現する方法として,「スレッド処理だけではなく他にも方法がある」ということに気づいていただけたら幸いです。
Reactorパターンの他にも何かあるのでは,と考えた方もいらっしゃるかもしれません。もちろん,他にも非同期処理を実現する方法はあります。Reactorとの対比としては,よくProactorパターンが取り上げられます。どんなものなのか,ぜひ調べてみてください。きっと興味を惹かれることでしょう。