N2Oについて
N2O(H2Oじゃないよ)はウクライナの Synrc Research Center という会社で開発されたWebアプリケーションフレームワークです。 N2Oは"Nitrogen 2x Optimized"の略で、同じErlangのWebアプリケーションフレームワークである Nitrogen から、主に 高速化を目的として分岐したプロダクトとの事です。 従って、Nitrogenを知っていれば、N2Oにもすぐ馴染めるのではないでしょうか。 Nitrogenプロジェクトでは文書が充実しており、それをほぼそのまま参考に出来るのは有利です。 個人的にはNitrogenも使った事がなかったのですが、N2O自体の文書も結構充実しており、特に困ることはありませんでした。 特に、N2Oについての68ページの電子書籍がPDFで公開されているのが大きかったです。 マイナーなフレームワークでもしっかりした文書があると安心感があり、入って行きやすいですね。 また、このN2O Erlang Web Stackという紹介スライドも明快で背中を押されました。 Synrc社の実例紹介ページによると、ウクライナの銀行、 PrivatBankのオンラインバンキングシステムに、N2Oを始めとした同社開発のフレームワークが活用されているとの事です。
特徴
N2Oは他のフレームワークと比べて、以下のような特徴があります。
- 高速
- バイナリ形式(BERT)のメッセージ送受信サポート
- テンプレートのサポート (ErlyDTL, Nitrogen, NITRO) ※ErlyDTLサポート ≒ Djangoのテンプレートが使える
- 実装がコンパクト (約1,100行)
私はErlang初心者なので、大きなフレームワークは多分理解不能、また規模が大きいとカスタマイズもしんどそうですが、千行位なら読み込めば何とかなるかも…。
N2Oでは、HTTPプロトコルのハンドリングにはCowboy, Pub/Subにはgproc 、テンプレートの処理にErlyDTLが使われています。
本体の行数が少ないのは、各機能を効果的にライブラリに委譲している事にも由来していそうです。 ところで、気になるのはやはり速度です。 少し前の情報のようですが、プロジェクトのページに以下のような表が掲載されていました。
もう一つ、N2Oのプロジェクトページの比較表から引用しますが、
Framework | Enabled Components | Speed | Timeouts |
---|---|---|---|
PHP5 FCGI | Simple script with two <?php print "OK"; ?> | 5K | timeouts |
ChicagoBoss | No sessions, No DSL, Simple DTL | 500 | no |
Nitrogen | No sessions, No DSL, Simple DTL | 1K | no |
N2O | All enabled, sessions, Template, heavy DSL | 7K | no |
N2O | Sessions enabled, template with two variables, no DSL | 10K | no |
N2O | No sessions, No DSL, only template with two vars | 15K | no |
もしPHPの2倍のスピードが出るのなら素晴らしいですね。これは是非試さねば。
動作確認環境
- サーバサイド
- Ubuntu 14.04 64-bit (AWS EC2 / t2.micro)
- Erlang 18.1 (Erlang Solutionsのビルド)
- N2O v2.11
- クライアントサイド
- Windows 7 64-bit
- Chrome 46
(おことわり)
このエントリは、半年ほど前、N2O v2.4で調査した際のメモを元に作成しましたが、 N2Oの最近のバージョン(多分2.8?)では結構大きな変更が加わったようで、サーバ・クライアント間で送受信するデータの構造やAPIが変わりました。 N2Oの最新バージョンは、2015/12/14現在、v2.11です。
引用したサンプルがv2.11でも動く事は軽く確認しましたが、内容が古い可能性があります(古いAPIを使っている等)。 時間が出来たら調べて内容を更新したいと思っています。 また、整理してからサンプル一式をGithubにソースをコミットする予定でいます。
お勧めのチュートリアル
まずはサンプルないかな? と、ググってみましたが、余りヒットしません。
Synrc社による実装例があるので、こちらを読むのが王道でしょうが、結構本格的なサンプルであるので入門用には重いです。
そんな中、以下のCRASHDUMP.IOによるチュートリアルが参考になりましたのでお勧めです。 4回シリーズで、ゼロから始めてHello World・テンプレートまでと、内容も軽めで分かりやすいです。
(ただし情報は若干古い)
作ってみる
以下を作ることを目標とします。
- 株価のリアルタイムレート画面
- サーバ側: Erlang + N2O
- クライアント側: ブラウザ、JS
開発ツールのインストール
ビルド時に必要になるツールをインストールします。
リアルタイムレートの提供元は?
少し探した範囲では、株価のレートを無料でリアルタイム配信してくれるサービスが見当たらなかったので、ビットコインのレートを使うことにします。 ビットコインの市場は、土日もなく24時間/365日動き続けているので、むしろこちらの方が都合が良いですね。
WebSocketを扱うので、wscat コマンドを環境に含めておくと良い感じです。
socat/netcatでも出来ない訳ではないですが、ちょっと面倒。
wscatのインストール
WebSocketからリアルタイムレート受信 (コマンド編)
BitfinexのパブリックAPIを使うと、Bitcoinのリアルタイムレートが取得出来ますので、こちらを使用します。
応答のフォーマットです。
リアルタイムレートが取得できたので、次は、このレートを情報源として接続してきたクライアントに配信するサーバサイドのプログラムを、N2Oで作ってみます。
WebSocketからリアルタイムレート受信 (Erlang編)
N2Oに組み込む前に、まずは単独でレートを受信してみます。 WebSocketのクライアントライブラリが必要ですが、今回は websocket_clientを使います。
(他にはGunも便利なライブラリだそうです)
単体で動かしてみます。
OK。Erlangでレートが受信出来ました。
N2Oでリアルタイムレート配信
続いて、N2Oを使った配信サービスの方を考えます。
先ほどのAPIで購読したリアルタイムレートを、接続してきたクライアントに配信(中継)すれば良いので、仕組みはチャットとかなり似ています。
(チャットは誰もが話しますが、レート配信は一人だけが話すイメージ)
N2Oのひな型にサンプルにチャットがありますが、このサンプルを改造すれば良さそうです。 ところで、N2Oには「レビューボード」のサンプルが付属しており、これはコードレビューを行うサンプルアプリで結構凝っています。 それとは別にもっと単純なチャットのサンプルがあるのですが、これはアプリのひな型を作った時に生成されます。
madでビルド・パッケージング・REPL
では、早速やってみましょう。
N2Oでは、rebarではなく、madというツールでビルド等を行います。
madでは、コマンドに、完全な名前ではなく始めの3文字を省略形として使用可能です。
N2Oアプリのひな型の作成
だいぶ雰囲気がつかめてきたところで、アプリの作成をやってみます。 アプリのひな型も、madで作成します。
このコマンドを発行すると、デフォルトでは、非常にシンプルなチャットアプリが生成されます。 生成されるものはもしかしたらカスタマイズできるのかも知れませんが、調べていません。 rate-ticker というプロジェクト名にします。
最後のコマンドの'repl'は、「REPLを開く」の意なので、シェル上でErlangのREPLが開いているはずです。 当たり前ですがここでコマンド等を試しに入力することも可能です。
設定ファイル
プロジェクトディレクトリ直下に設定ファイルがあるので、必要に応じて調整します。
— rebar.config
— 利用するライブラリの定義
— sys.config
— パラメータの定義
ファイルの内容を見れば一目瞭然なので、説明は省略します。
ブラウザで確認
ここまでで、サーバのポート8001番で最低限のWebアプリケーション(チャット)が動いていますので、 ブラウザでアクセスしてみます。 複数のブラウザで開いてみて、あるブラウザで入力したメッセージが、全てのブラウザに正しく伝わるかも確認してみましょう。
ブラウザ上で何かメッセージを入力してから、CHATボタンを押すと、サーバにはシリアライズされたデータが送信されてきます。
単純なメッセージでも、結構複雑な形式のデータとして送られています。
サーバ側で確認
サーバ側のシェルセッション上にはErlangのREPLが開いており、INFOレベルのログが出力されるので、何が起きているか確認できます。 また、ここでENTERキーを押すと、対話型のシェルに入りますので、Erlangのコードを入力可能です。 試しにコマンドラインからメッセージを送ってみましょう。
無事ブラウザに「message!!!」が表示されたでしょうか?
バイナリフレーム送受信の確認
もうちょっと詳しく見てみましょう。 クライアント側にどのようなメッセージが来ているか、ブラウザの開発者ツールで確認してみます。 Chromeで、F12キーを押して開発者ツールを開き、Network - Framesを選択、WSタブをクリック。 オレンジ色で、Binary Frameという列がありますから、確かにバイナリでデータが届けられました。
次に、デバッグプリントを有効にして、コンソール上に詳細情報を出すようにします。 F12キーを押して開発者ツールを開き、Sourceペーンのn2o/protocols/client.jsをクリックします。 これはサーバからのメッセージを読み込む処理の一部です。 client.jsのソースを見るとすぐ分かる通り、debug という変数が真ならデータがコンソールにデバッグ出力されるようになっているので、debug変数を定義しましょう。 debug変数は、別のファイル、n2o.jsに定義されています。 Chrome上でテンポラリでJSを編集しても良いですが、毎回やるのも面倒ですので、サーバ側のJSを編集してしまうのがお勧めです。
関係ないですが、サーバ上でファイルを編集すると、madが自動的にリロードしてくれるため、アプリの手動再起動が必要ありません。 ブラウザの方をリロードするだけで大丈夫です。これは大変便利ですね。 (もしそうならない場合は、inotify-toolsをインストールして下さい)
ブラウザのコンソールに生っぽいデータが表示されたと思います。 N2Oではデータの送受信をBERTというバイナリ形式で行っています。 クライアント側だと「protocols/bert.js」を読むと雰囲気がつかめると思います。 ブラウザの開発者ツールで、client.jsを開き、適切な場所にブレークポイントを設置すると生のデータが確認できます。 例えば、メッセージはこのような形式です。
これはなんでしょうか。
この例では空なので見てもよく分からないですが、この部分にはJavaScriptの命令文が入って来ます。結構ユニークですね。 ザでevalするだけ で、フォームに値がセットされたり、文字や背景色が変わったりという事に使う事が出来ます。 データを送ってもパースしてからどこかにセットするわけなので、サーバから送られる時、命令文を組み立てて置けばよい、 ということでしょうか。この発想は私には目から鱗でした。NitrogenのPushBack方式、と解説されていましたが、 SPAで良くあるやり方なのか、Nitrogenのやり方なのかはよく分かりませんでした。
dataも、生の値ではなく、BERT形式でエンコードされています。 ザコンソール上ではdec()関数でデコードできます。
試しにコンソールからカスタムメッセージを送ってみましょう。 ッセージを送るだけでも、結構複雑なデータ構造を与えないといけません。 ンの値は、接続時にサーバ側から送られているので、ブラウザの開発者ツールで確認して値を置き換えて下さい。 ンの値が間違っていると、サーバ側で無視されますので、何も起きません。
上手く行くと、以下のように送ったメッセージがそのままサーバから返ってきてブラウザ上に表示されるはずです。
N2Oのアーキテクチャ
リアルタイムレート画面の作成
簡単なサンプルチャットアプリを動かして、自動・手動のメッセージ送受信まで出来ました。 アルタイムレート配信サービスを作成します。 的にはチャットとレート配信は似ているため、チャットのサンプルを改造すれば良さそうです。 のパーツとしてJSのライブラリ等を使うため、Webアプリの「ルーティング」を調整する必要があります。 画面のテンプレートの作成・処理も必要です。 配信部分。単体で動作確認した、リアルタイムレートをAPIから取得するソースを使います。 wf_bitfinex.erlをプロジェクトに組み込み。
ws_bitfinex.erlを調整します。レート受信時に、N2Oのwf:send()を呼び出して、クライアントにブロードキャストします。
再ビルド・起動します。
リアルタイムレートの確認 (ブラウザ)
画面周りのテンプレート作成
リアルタイムレートがブラウザに配信され生で表示されるところまで来ました。 あとは綺麗に表示すれば完成なのですが・・・ 元々作っていたサンプルがN2O v2.11で動かなくなってしまったため調整中です。 すみません!
** TBD **
小ネタ集
マルチクライアント対応 (Pub/Sub)
※この手順は昔のバージョンでのみ必要です。N2O v2.11では標準でこのように動作するようになっていました
標準のチャットのサンプルは、1 対 1の通信をするものとなっていますので、 もっとチャットらしく、1 対 多のPub/Subの通信をするよう変えてみます。 N2Oには、Pub/Sub用APIが用意されていますので、呼び出すだけ使えます。 (Pub/Subの実装はgprocが利用されています)
- wf:reg — api.htm
HTTPS化
※ N2O v2.4の場合。要更新
ベンチマーク
** TBD **
(改めて)N2Oが実現している機能
サンプルを動かして気付いたのは以下の機能です。
- WebSocket + XHR fallback ← サーバを落とすと、JS側でXHRによる接続が試行される
- バイナリでのメッセージ送受信 (BERT)
- 簡易暗号化 (pickle)
- セッション
- PING/PONGは自動で4秒毎に発行される
- サーバからJSの式を送りクライアントで評価する仕組み(多分Nitrogen由来の機能)
N2Oを使ってみた感想
N2Oはかなり魅力的なライブラリに感じました。
- シンプル。覚えるべきAPIが少ない (wf.erlを読めば大体分かる)
- バイナリ送受信標準サポート(取り外し可能。JSONに切り替える事も出来るらしい。未検証)
- ハートビート機能が標準で付いてくる (4秒毎のPING/PONG、こちらも取り外し可能)
- 速さ (注: 体感) ※要ベンチマーク
今回紹介した機能を実現するため実装したのは、ソースコードの行数で言うと50行程度。 あまりにあっさり出来るので正直拍子抜けする位でした。 また表現力も高く、SPAのアプリの中にはErlangの一ファイルですっきり実現出来るものもありそうです。 (N2Oのトップページで紹介されているchat.erlのイメージ) 個人的には、N2Oは一見シンプルですがフレームワークの機能は豊富だし奥が深そうに感じています。 一点、N2OではWebSocketが使えないクライアントのために、XHRによるフォールバックのエンドポイントも用意されているのですが、 v2.4の頃は安定性に少し問題があり、開発者も修正に積極的ではない様子でした。
実際、チャットのサンプルをWebSocketとXHRで動かした時に、XHRの方にだけメッセージが来ない現象がありました。 、最近のバージョンでは n2o_streamというエンドポイントが新規で追加されているので、改善されている可能性がありそうです。
終わりに
N2Oは利用者がまだそれほど多くなく、ライブラリの安定性や高負荷時の挙動については別途評価が必要と思われますが、 WebSocketでリアルタイム通信をするようなアプリでは、候補として検討してみてはいかがでしょうか?
今回は、長い割にはN2Oを駆け足で紹介しただけで終わってしまいました。
WebSocketに加えてHTTP/1.1 Chunkedでの配信する、等も試してみたので何かの機会に紹介できたらなと思います。 angとN2Oを使って、将棋ウォーズやlichessのようなリアルタイム対戦サービスを作るのが私の来年の目標です。 はみなさん、メリークリスマス!
参考資料
- N2O v2.9 作者による解説
- BERT BERT規格
- gproc - Extended process registry for Erlang gprocの解説
Credits
Original source — https://qiita.com/dseg/items/4f2c...
Author — Daichi Shinozaki — @dseg