BT

最新技術を追い求めるデベロッパのための情報コミュニティ

寄稿

Topics

地域を選ぶ

InfoQ ホームページ アーティクル RubyのFiberを非同期I/Oに使うNeverBlockとRevactor

RubyのFiberを非同期I/Oに使うNeverBlockとRevactor

Fiber(参考記事・英語) は活用できる新しい並行性プリミティブ(もっとも基本的な要素)としてRubyプログラマの中で徐々に認識されだしています。その中で、ユーザ空間スレッドやRuby 1.9のGiant Interpreter Lock(GIL:Ruby言語のスレッドが1度に1つしか走らないようにする)の問題に対処するのに、Fiberとノンブロッキングあるいは非同期の I/Oを組み合わせた方法を採用する2つの試みがあります。

ブロッキングシステムコールの一番の問題は、ほぼI/Oに関することです。readのようなブロッキングシステムコールでは1度に1つのデータしか返りません。するとユーザ空間でスレッドを実行するシステムでは、ひとつのプロセス内の全てのスレッドがブロックされることになります。解決策はI/O要求をI /Oのブロッキングと切り離すことです。その一つの方法がブロッキングシステムコールが実行される前にI/O要求をキャッチして、このI/O要求をノンブロッキングな形で送り、要求元のFiberを一時停止させて別のFiberに実行権を与えるやり方です。そしてこのI/O要求に対しての応答をシステムが受けたら、元のFiberを再度スケジューリングすることになります。

Fiberはユーザに試練を課すことなくこの考えを実装する有益な並行性ツールとなります。今Fiberを使ったソリューションを実現するためのライブラリが2つあります。まさにこのためのソリューションとしてあるのがNeverBlockライブラリ(リンク)です(GithubにNeverBlock(リンク)、NeverBlock PG(リンク)、NeverBlock PG adapter for ActiveRecord(リンク)、非同期操作をサポートするMySQLアダプタのMySQLPlus(リンク)のレポジトリがあります)。より汎用的なソリューションとして、ErlangのActorや通信プロセスなどの考え方に基づいたTony Arcieri氏によるRevacotrライブラリがあります。私たちはNeverBlockプロジェクトのMohammad A. Ali氏(リンク)とRevactorライブラリのTony Arcier氏(リンク)に話を聞きました。

NeverBlock

InfoQ:NeverBlock がすることを正確に言うとどういうことになるでしょう?私が見る限り、Fiberに格納されたコードをコネクションプールに渡して、コネクションが利用可能になるとそれらFiberの実行を再開できるようにするのがNeverBlockのようです。NeverBlockの一番の利点はなんでしょうか?
 

Mohammad A. Ali:プーリングの機能はNeverBlock以外のところが適切に機能するために必要だったものです。全てのものを一つのところに集めることで、Rails、 Merb、Ramazeのようなアプリケーションがスレッドセーフのフルセットをもたなくても即座にI/O並行性を利用できるようになります。

これを実現するために、NeverBlockのFiberプールから取得したFiberで要求をラップするウェブサーバと、それらのFiberを認識して、中断や再開の要求に応じて扱うことのできるI/Oライブラリ(DB操作だけでなく全てのI/O操作をおこなう)が必要でした。そして EventMachineあるいはRevのようなイベントループ(イベントを監視する機構)を使ってそれら全てを調和させる必要がありました。


InfoQ: PG はNeverBlockとPostgreSQLドライバを使っていますが、PostgreSQLはすでにノンブロッキングI/Oを装備していますね。この場合NeverBlock PGはどのような役割をもっているのでしょうか?もしMySQLのようなノンブロッキングI/Oがなさそうなもの用のドライバを使うとしたら、 NeverBlockはどのような手助けとなるでしょうか?

Mohammad A. Ali:NeverBlock::PGはノンブロッキング操作を透過的におこなえるようにしますが、そのためにはそのFiberを次のようにNeverBlockのFiberプールで作る必要があります。

pool.spawn do
 res1 = db.exec(query1)
 res2 = db.exec(query2_that_depends_on_query1)
end 

ノンブロッキングを実装する大抵のものではこれよりずっと複雑なことをしないといけません。Fiberのおかげで一見ブロッキングがあるように見えるコードを書くだけでノンブロッキングを実現することができます。

今わたしたちはNeverBlock::PGドライバをさらに先へ進めようとしているところです。ちょうど新しいActiveRecord- NeverBlock-Postgresql用アダプタをリリースしたところで、これによってActiveRecordにノンブロッキングI/Oがもたらされます。この次にNeverBlockがターゲットにするものが何かは簡単に推測できると思います。

NeverBlockの特徴的な点は、他のプログラムに適用してもほとんど違いが分からないだろうということです。

NeverBlock はブロッキングをおこなうプログラムをノンブロッキングにすることはできません。NeverBlockがおこなうのは、ブロッキングするように見えるコードでも、ノンブロッキングの特性を損なうことなしに、ノンブロッキングをおこなうプログラムを書けるようにすることです。ですから、将来に向けて Asymy(非同期型のMySQLドライバ)のような取り組みについても大変注目しています。

編集部注:このインタビューの後に非同期型のMySQLドライバの最初の試みとしてMySQLPlus(first attempt at an asynchronous MySQL driver, MySQLPlus)が公開されました。



InfoQ: このライブラリで必要なFiberはRuby 1.9でしか使えません。このことが(Ruby 1.9はまだ多くの変更がおこなわれていて、まだそれほど多くの採用はされてないと考える)ユーザにとって問題となるという考えはありますか?このライブラリあるいは一般的にはFiberの利点がRuby 1.9を採用するきっかけとなるとお考えでしょうか?

Mohammad A. Ali :私はRuby 1.9で生まれた最良のもののひとつがFiberだと考えています。同じ機能はRuby 1.8でもcontinuation(継続)を使って可能ですが、パフォーマンスが悪いという面があります。ともあれ安定版の1.9がリリースされようとしていますし、私は全員が1.9に移行すべきだと強く思っています。NeverBlockやRevactor、その他類するものによる利点が、人々の移行を決心させるくらいのものであってほしいと願っています。


InfoQ:  NeverBlockのソースコードについてですが、NeverBlockではFiberクラスを拡張していくつかのメソッドを追加していますね。これはなぜですか?

Mohammad A. Ali :Fiber によって複数のスレッドを使う場合、Fiber内のローカル変数を保持しておくことに関してFiberはあまり融通が効きません。トランザクションを実行しているActiveRecordを切り替えるためにはこのような機能が必要でしたし、そのことがFiberに分かるようにする必要もありました。また Fiberでの処理でもノンブロッキング処理をするかしないかを選択できるようにするのにも必要なことでした。


InfoQ: Revactorについて耳にしたことはありますか?

Mohammad A. Ali :ええ、私はRevactorに少し関わったことがあります。それにNeverBlockではEventMachineだけでなくRevを第2のバックエンドとして(まだ実験段階で)実際に使っています。しかしNeverBlockの目標はRevactorとは異なっています。RevactorはRubyに新しい並行モデルをもたらすものですが、NeverBlockは現行のRubyプログラムに最低限の変更を加えるだけで並行性をもたせるためのものです。


編集部注:非同期のMySQLドライバーであるMySQLPlusの初期バージョン(リンク)はこのインタビューの後にリリースされました。

 

Revactor

InfoQ: Revactorの現状はどうなっているでしょう?

Tony Arcieri: 他の無数にあるプロジェクトと比べると少し注目度は低いですが、それでもちゃんと商用利用されているほどになっています。


InfoQ:  Revactorの将来のバージョンでは何が予定されているのですか?

Tony Arcieri: 最近Aman Gupta氏がRuby 1.8向けに「哀れな人たち用Fiber」実装をリリースしました。同じようにRevactorをRuby 1.8にポートすることも可能かもしれませんが、パフォーマンスで苦しむことになりそうです。

今のところRevactorではすべてのActorが同一のRubyスレッドにあることを前提にしています。ほぼ全体において異なるスレッドにある Actorのメッセージ送信は可能になっていますが、まだそれは完全でなく有効になっていません。私はこの機構がサポートされることに関心のある人たちと話をしてきているのですが、早いうちにその成果がRevactorに取り入れられることを願っています。


InfoQ: ユーザはRevactorのActorを直接使ってプログラムする必要があるのでしょうか。それともRevactorのActorを他のライブラリのバックエンドで使える、つまり(ディベロッパが)透過的にRevactorのActorを使えるようになっているのでしょうか?
 

Tony Arcieri:  Revactor はRubinius(Smalltalk風のバイトコードによるRuby実装)でのActor実装と互換性がありますが、現段階ではRubiniusの Actorを使ってErlangのgen_tcpほど簡単にネットワークプログラミングができるような状況ではありません。とはいえ、Actorを使ったネットワークアプリケーションを書いているプログラマの目がRevactorに集まり始めたところですし、いずれその人たちのアプリケーションを Rubiniusに持ってくることもできるようになるでしょう。


InfoQ: ブロッキングをおこなうI/O要求のスケジューリングはどのようにおこなわれているのでしょう?I/O要求を実行するのにカーネルスレッドをいくつか使っているのでしょうか?
 

Tony Arcieri:Actor から送信された全てのメッセージの処理が終わっていて、かつメッセージを送ったActor以外にActorが実行可能になっていないなら、 Revactorは私が書いたRevという(EventMachineに似た)ライブラリを使ってI/Oイベントを管理します。RevはRuby 1.9のrb_thread_blocking_region()を使って、I/Oの利用可能状態を知るためにブロッキングシステムコールを監視するので、別のカーネルスレッドを作る必要はありません。RevactorにはRubyのTCPSocketクラスをダックタイプするクラス(Revactor::TCP)があり、これによってブロッキングシステムコールがされた時にそれをActorスケジューラに送って保留するようにしています。Rubyのソケットを使っている既存ライブラリがRevactor::TCPを使うようにするモンキーパッチ(元のコードを変更するのでなく実行時に変更するパッチ)を作るのは簡単でした。その一例をあげると、RevactorはMongrel(Webサーバ)に小さなモンキーパッチをあてて並行処理にThreadでなくActorを使うようにしています。


InfoQ: I/O要求を連続して出すコードにはどのように対処しているのでしょう?リクエストをひとつにまとめるのでしょうか?

Tony Arcieri: 表面的にはそのような要求で「ブロッキング」が起きているように見えます。I/O要求が出された時に、実行中のActorは中断されI/O処理が完了した後に再開するようスケジュールしなおされます。これによりブロッキングが不可欠なインターフェースに依存しているライブラリでもRevactor上で効率的に動かすことができます。逆に現状Revactorで動かすことのできないものとしてはActiveRecordやDataMapperがあります。


InfoQ: RubiniusのActorについてはどうでしょう?

Tony Arcieri: Rubinius のActorもTCPSocketの「アクティブモード」を使えばRevactorができることは全てできます。これはあるTCPSocketからの入力を必ず読み取るのではなく、標準の内部Actorメッセージングを使って、非同期で決められたActorに入力データが渡されるということです。これによりActorでI/Oと内部Actorメッセージを並行して処理できます。RubiniusのVMは今C++で書き直されているとこで、これが終わった時に「アクティブモード」メッセージ配信を実装するのに必要な全ての機能が揃うことを期待しています。それらが整えばRubiniusのActorの改良をおこないたいと考えています。


InfoQ:  RubiniusでRevactorをサポートするようにする計画はありますか?

Tony Arcieri: いいえ、RevactorはかなりYARVの機能に依存しています。RubiniusはTaskおよびChannel方式による優れた並行処理モデルと I/Oサポートを備え、既存のActor実装でもこの両方有効利用することができます。RevactorとRubiniusのActorはお互いをかなりダックタイプしているので、双互換性のあるプログラムを書くのもさほど頭を悩ますことではないはずです。


InfoQ: ActorやRevectorを(Ruby 1.9でなく)Rubiniusで動かすメリットはなんでしょう?

Tony Arcieri: 差しあたりRuby 1.9の方が一般的にパフォーマンスがよく既存のライブラリとも相性がいいです。Rubiniusは発展途中で目下書き直しをおこなっているところです。将来的にはRuby 1.9以上に多くのメリットを持つようになるでしょう。並行処理やI/OのTask/Channel抽象化がRuby 1.9にあるものよりずっと洗練されているからです。Rubyは1.8でも1.9でもEventMachineやRevのようなイベントフレームワークと Ruby標準のI/Oが当分並行して使われるでしょうが、Rubiniusでは最初から高度なI/Oを備えています。


InfoQ: Revactorを使っているプロジェクトをなにかご存知ですか?

Tony Arcieri: 内部のプロジェクトで使っている多方面の人たちと話すことがあるのですが、ほとんどは並列HTTPクライアントとして使っています。公開されているプロジェクトでRevactorを使っていると聞いたことはありません。


InfoQ:Ryby 1.9あるいはFiberを使ったライブラリに依存していることがRevactorの採用の妨げにならないでしょうか?

Tony Arcieri: それはあると思います。Ruby 1.9に関連したバグは相応にありましたし、ほとんどの人がRevactorを使ってみることにはかなり慎重だと思います。しかし、これまでも多くのプロジェクトが後になって急に現れるということがありました。そういうプロジェクトではFiberを使ったイベントフレームワークと組み合わせていて、たとえばRy Dahl氏のFlowウェブサーバなどがそうです。

 

原文はこちらです:http://www.infoq.com/articles/fibers-neverblock-revactor
(このArticleは2008年8月28日に原文が掲載されました)

この記事に星をつける

おすすめ度
スタイル

BT