uirouのひとりごと

不定期に何か書こうと思います。

node.js 使ってみた

ながいながいまえふり(読み飛ばし推奨)

(関数型言語使ってないけれど)最近、プログラムを書くときには関数型言語で書く、もしくは関数型っぽく書くといろいろ考えることが減っていいんじゃないかと思っていて、それと関連して Erlang とかのアクターモデルが私の望んでいるスタイルにぴったりなんじゃないかと思っているわけです。

というのは、thread とか使ってる時に mutex lock とかしなくちゃいけないとかもうね。考えたくないわけですよ。これって関数型言語でよく言われる「副作用が無い」という状態にしておけば、そもそも mutex lock なんて概念は使わなくていいわけです。たとえば、五月雨式にやってくる要求に対して個別に thread を立てて対応をする、ってな事はよくやるわけですが、この時作る thread を副作用の無いものにしておくと mutex lock とかしなくて済んでいいですよね == 考える事減るよね、となるわけです。
といっても、それだけではうまく動かなくて、副作用のある事が書きたくなったりするわけです。例えば、複数のアクセスが同時に来るときに、そのうち一つだけに何か特別な値を返したい時です。この例だと「特別な値を返した事があるかどうかを記憶している部分」を共有して、書き換えないといけない、ということで、副作用を伴う事になります。ここで、アクターモデルだとアクター間でのメッセージパッシングという仕組みでこの副作用をある程度解消できているんじゃないかなぁと思っています。
というのは、アクターモデルだとアクターごとに状態がある事になる……のかな、たぶん。……のですが、とりあえず副作用の無い部分は関数の形で実装して、副作用が発生する所で他のアクターに対してメッセージを投げる、という動作にしてしまえば、個々の関数に関しては副作用の無いものが書けるのかなぁ、という気がしています。例えば、前の例だと複数のリクエストを受けた時にそのリクエストの数分だけアクター A, B, C を作って、それらのアクター A, B, C がある一つのアクター α にメッセージを投げると、そのアクター α が持っている内部状態を更新しつつ、アクター A, B, C それぞれに別のメッセージを投げ返す(うち、一つは特別な値を返す)、という事で実装すればいい、という事になります(蛇足: この場合はアクター α は副作用がある関数を実装していることになります。実際の所、Erlang の分散DB Mnesia はそういう感じで動いているっぽいです)。
ということで、「副作用がない関数を書こう」というのと、「アクターモデルってイイカモ?」というのが最近私がかぶれている概念なわけです。

あ、Erlang というか関数型言語自体、全然使っていないため、ここまで書いてきた事は全部私の理解したつもりになっているにわか知識からの想像です。ということで嘘八百が書いてあるかもしれないので鵜呑みにすると大変なことになるかもわかりません。というかよくわかってる人はコメント欄ででも間違いを指摘して晒しあげておいて頂けると嬉しいです。もちろん、こっそり教えていただくのでも構いません。:D

で、です。Erlang すごいじゃん Mnesia は真の分散DBだぜ(CodeZineの記事の冒頭による)、凄いぜすごいぜ最強なんじゃね?と思って Erlang を使ってみようかなぁと思ったのだけれど、いろいろと覚えなければならないことが多くて大変だなぁ、と感じていじれていなかった所に、「node.js ってなのがあってー」という話題を聞きつけたり、node.js についての Erlang のコミュニティの反応 みたいな例を見たりして、案外 node.js もいいかもわからんね?なんか勢いあるみたいって所とか手続き型っぽい書き方とかそれでもクロージャが書けるのでメッセージパッシングっぽい何かとして非同期I/Oを隠蔽してるし、私としては JavaScript なら Erlang より書きやすいし。とか考えて、node.js が気になっていたわけです。

ということで、node.js をとりあえず使ってみようとしてみましたよ、というお話です。

node.js とは

node.js は「JavaScript で書けるサーバをつくるための何かのうちの一つ」のようです。以下のような特徴を持ちます。

  • 非同期I/O の塊なので、基本的にクロージャを使っての関数呼び出しの連鎖でアプリケーションを記述する(たぶん、このあたりは jQuery を使ってた人たちとかは馴染みやすい気がする)
  • V8 JavaScript Engine を使っているので JavaScript といっても遅いわけではない
  • libev を使って 非同期I/O を実現している(多分 C10K問題 への対応はOKな気がする)
  • JavaScript というだけあって、Web屋さん方面の人々とかが押し寄せてきているらしく、活気があるっぽい?
  • npm というモジュールマネージャがあって、なにやら便利なモジュールがいっぱいある(sudo curl http://... | sh ってするとインストール完了ってどうなのよ…… とかいうのはちょっと気になるケド)

開発環境を手に入れる

node.js 本体を手に入れる

http://nodejs.org/ から先日リリースされた v0.4.0(stable release は 0.(n*2).* (n は 0, 1, 2, ...)らしいです)を download してきて

% ./configure; make; sudo make install

で install は完了です。
あ、make するのに python とか libopenssl とかがあるといいです(python は必須)。
何故か手元の Windows7 64bit版 では configure の時点で python が変なエラーを吐いて駄目でした。まぁ localhost で動く必要はなくなったのでいいですけれども。

npm を手に入れる

npm は node.js のモジュールをインストールしやすくするためのものです。

% curl http://npmjs.org/install.sh | sudo sh

で、install は終了なんですが…… ネットワークの先の sh script をそのまま sudo した sh に食わせるのって…… と思いますた。
でも、その後で

% sudo npm install express

とかしちゃってるのでもうイイですよねたぶん。

node-inspector をインストールする

node-inspector は node.js のデバッグをできるようにする何かです。
すにぺっとさんの node.js でのデバッグ方法 という記事が分かりやすかったのでそこを読んでおくといい気がします。

すにぺっとさんの所でも書かれているのですが、このデバッガは Google Chrome等 の Webkitを使ったブラウザが必要です。

すにぺっとさんの記事の時点では 0.4.* は出ていなかったので少しトリッキーな事をしているようですが、もう 0.4.* が出ているので何も考えずに

% sudo npm install node-inspector

とするだけで良いです。

その他、使いそうなモジュールをいくつか入れる

今回の記事部分では使いませんでしたが、google先生 にいろいろ聞いていた所、express というフレームワークejs というテンプレートエンジン、socket.io が便利そうなので、npm で入れておきました。

% sudo npm install express
% sudo npm install ejs
% sudo npm install socket.io

socket.io については、クライアント側のものも必要っぽいです。

とりあえずデバッグ実行してみる

デバッグ環境の node-inspector の使い方が少しトリッキーでした。というのは、

  • node-inspector は node とは別に起動しておく必要がある
  • node-inspector は localhost からの接続しか受け付けないので、chrome の動いている host と別の host から使うには ssh の port forward のような事をする必要がある

という点でした。

私の場合、

localhost% ssh -L 8080:localhost:8080 node-inspectorの動いているホスト

という形で ssh で port forward させて、http://localhost:8080デバッグ用にひらいて解決しました。

ということで、起動する順番としては、

% node-inspector &
% node --debug hoge.js

というような感じで node-inspector と node.js を実行して、chromehttp://localhost:8080/debug?port=5858 とか(default なら http://localhost:8080 で ok)を開くとデバッガの画面が表示されました。

ブレークポイントで止めたあと、変数の中身とかも詳しくみられます。

いいかんじです!

ちなみに、複数人で同時に同じホストで開発する時とかではポート番号がかぶってしまいそうですが、その場合は node --debug への引数としてデバッグ用のポート番号を指定して、node-inspector には URL の後ろに /debug?port=... の形式でそのデバッグのポート番号を指定すれば良いようです。
具体的には、

% node-inspector &
% node --debug=15858 hoge.js

としたのなら、http://localhost:8080/debug?port=15858 へとアクセスすれば、デバッガへと接続できます。

とりあえず、これで初期設定としてはいい感じでしょうか。うまく何かが作れたら続くかもわかりません。