YAMAGUCHI::weblog

噛み付き地蔵に憧れて、この神の世界にやってきました。マドンナみたいな男の子、コッペです。

なぜErlangなのか

はじめに

最近はErlang関連のWeb書籍の翻訳をしてまして*1Erlang関連のニュースなどを収集して読んでいます。そこでたまたま見つけた面白そうなエントリがあって、翻訳しようかなと思っていたら@voluntasの兄貴ご推薦ということなので、日常生活を取り戻すべく翻訳してみました。
このエントリはinagistというサービスの公式ブログで、ErlangWebサービスを作ることの利点を説明したエントリです。多少補足や外部リンクが必要な部分は僕がリンクを貼ったりしているので、わからない場合はご参照ください。あと翻訳が怪しいところがあるのでツッコミ歓迎。

なぜErlangなのか

よく私がinagistはErlangで書かれていると言うとおかしな目で見られることがあります。なので、ここでErlangが適している重要な点をいくつかあげようと思います。

私たちがしていること

inagistでは、リアルタイムでツイートストリームを要約しようとしています。今のところはTwitter Streaming APIを使って動作させています。要約することによって、リアルタイムにストリーム中で人気があるツイートをフィルタリングして、トレンドに応じてツイートをグループ分けしています。これを実際にjustinbeiber.inagist.comlibya.inagist.comで見てみましょう。(WebSocketを使っているのでGoogle ChromeSafariが最適です。)こういった要約を、いくつもの方法で組み合わせたストリームに対して行っています。ユーザ独自のストリーム(マイストリーム)、キーワード検索ストリーム(libya.inagist.com)、キーワード+ジオロケーションベースストリーム(sxsw.inaginst.com)がそれです。

軽量プロセス

ここではっきり意識しておかなければいけないのは、ツイートストリームを要約するときのリアルタイム性です。ストリーム中の個々のツイートを可能なかぎり永続化して、オフライン分析をするという方法もあります。しかしここでは代わりにユーザ毎に有限なキャッシュを作って、あるツイートが人気がでたり、トレンドを検知するときにストリーム中でキーワードが繰り返されたら、キャッシュに押し込んだり取り出したりしたりし続けています。これがErlangに適している部分なのです。ストリームのコンシューマはErlangプロセスとしてモデル化されています。理由はErlangプロセスが軽量で、独立しているからです。本質的にはユーザ毎に用意されたプロキシです。このErlangプロセスはストリームからツイートを受け取って、キャッシュを操作して、データを返すためにAPIクエリに応答します。これらのストリームコンシューマはそれぞれgen_serverによる実装になっていて、supervisorチェインに結びついています。コンシューマが落ちたときには、supervisorが他のユーザに全く影響を与えずに再起動します。

メッセージ

では、どうやって取得したデータストリームとこの軽量プロセスを、メッセージを使って結びつけるのでしょうか。個々のツイートはコンシューマにStreaming APIクライアントからのメッセージとして届けられます。ツイートのコンシューマはそれぞれErlang VM上全範囲に渡るプロセスツリーの一部となっています。ツイートがネットワークから取得された瞬間に、ツイートが別のプロセスとして起動されて、JSON化されたツイートを処理して、分散されたプロセスツリーにメッセージを投げます。メッセージはコンシューマプロセスに渡されて、コンシューマプロセスがキャッシュの更新を行います。非同期メッセージなので、クライアントではツイート一つに対してどれだけコンシューマがあるかは関係ありません。コンシューマプロセスがあるツイートに関連があるなら、ただ処理できるのです。メッセージは分散して送出されているので、取得するツイートの負荷が大きくなったときやコンシューマの数が増えたときにはスケールします。

分散Erlang

コンシューマの数が増えるにつれて、Erlangの他の重要な側面が役に立ってきます。マシン間での分散です。コンシューマプロセスは設計においてはプロセスIDでしか認識できないようになっています。ErlangではローカルプロセスIDと分散プロセスIDを同様に扱います。コンシューマが分散ツリーの一部であるかぎり、ツイートはプロセスに届けられます。これはスケールアウトを容易にします。個々のマシンはクラスター全体に影響を与えることなく独立して落ちることができ、ただそのマシンが落ちている間はそのマシン上に配置されたユーザがオフラインになるだけになります。

リアルタイム配布

出来るだけリアルタイムにするために、接続されたクライアントにはWebSocketを使ってメッセージを送ります。ここでメッセージが再び登場です。ストリームコンシューマはそれぞれメッセージを生成して、キャッシュが人気のあるツイートやトレンドを明らかにします。我々のアプリのGoogle ChromeプラグインはWebSocketクライアントで、この配布モデルを使っています。Chrome拡張はトレンドがわかったり、ツイートにある程度以上人気が出たら通知をします。また拡張を使って別の角度からもリアルタイム検索をしています。トレンドがわかったときに、拡張が自動的にそのトレンドに沿った目立ったツイートを探し始めます。

ストリーミング検索

以前に検索ストレージとしてどのようにRiakを使っているかに触れました。それに加えて、ストリーミング検索を可能にする独自の拡張を行いました。Riakでドキュメントにインデクスを張るときに常にインデクスデータを検索基盤に送ります。またこのインデクス項を、ツイートが別のインデクスに取得されたらときにもすぐに送ります。ここでシステム上には、パターンマッチをするために のタプル待っているプロセスや、検索基準に合致するドキュメントのプロセスを待っている通知があります。いまのところ ==, and, or, >, <, >=, =< といった演算子をサポートしていて、sxsw(==)、justin beiber(and)、文字列を含んだドキュメント、バウンディングボックス内の緯度経度など、どんなツイートでも検知できます。ストリームコンシューマは、これを使ってストリームからリアルタイムにフィルタされたツイートを取得します。Chromeプラグインでもこの検索ストリームをつついて、検出されたトレンドや検索クエリにあったのツイートがあったときにはいつでも、ユーザに通知をします。この機能は本当に強力です。なぜなら、これがあればトレンドを見ることでユーザがどんな話題に興味があるかとか自動的に知ることができ、ユーザにリアルタイムにこういった話題に合致するものがあったときにいつでも知らせることができます。このストリーム検索は、メッセージベースのアーキテクチャの性質によって、小さいオーバーヘッドで動作しています。この性質によってブラウザで1秒か2秒くらいでストリームを処理することができるのです。justinbieber.inagist.comなどのページのヘッダにある"Live Stream"をクリックすれば、実際に動作しているところを見ることが出来ます。

おわりに

以上、Erlangの差別化要因に関して俯瞰してお話しましたが、わざわざErlangを使っている理由はご理解いただけかと思います。もしもっと情報が必要ということであれば @jebui までコメントをください。喜んでお返事致します。

追伸: さっきツイートストリームで話題になっていたJustin BieberかLady Gagaのアルバムはまだ聞いてません。

訳者感想

当たり前な話ですがErlangは誰でも簡単にスケールするアプリケーションが書けるわけではなく、やはり書くためにはそれなりに経験が必要だと思いますが、言語/標準ライブラリ自体がそれを意識した作りになっているので、きちんと勘どころを抑えれば安全に早くそういったものが書ける土台があるという点で素晴らしいですね。これから始める方に、ご紹介。

    • 拙訳のErlangのWeb書籍とりあえず始めてみる方にはおすすめです

そしてちゃんとやるんだったら戦闘機本がおすすめですよ。

プログラミングErlang

プログラミングErlang

*1:遅筆ですみません