May212012

Socket.IO で RangeError: Maximum call stack size exceeded #nodejs_jp

Node.js v0.7 系から、 EventEmitter の変更が原因で、 Socket.IO が落ちるバグが発生しています。

具体的には Socket.IO@0.9.6(およびそれ以前) を node@0.7.8(及び 0.7.x 全般) で起動した後、ブラウザからアクセスしたら、下記のようなトレースを吐いて落ちます。

Jxck$ node server.js
info  - socket.io started

/../gist-2292777/node_modules/socket.io/lib/manager.js:0
(function (exports, require, module, __filename, __dirname) { /*!
^
RangeError: Maximum call stack size exceeded

上記を再現できるソースはこちら。なんでもないサーバ実装です。

https://gist.github.com/2292777

このバグの原因は、Node.js 本体の  EventEmitter にあったこの修正です。

https://github.com/joyent/node/commit/78dc13fbf97e2e3003e6f3baacdd5ff60e8de3f7

これにより、Socket.IO サーバ内で無限ループな感じになって、スタックが食いちぎられます。

そして、対応する Socket.IO への修正はこちら。

https://github.com/LearnBoost/socket.io/commit/e1884859bcbb57daeb843421cc5b4b95bd914bd8

しかし、socket.io@0.9.6 リリースの直後にコミットされて、まだ socket.io@0.9.7 がリリースされていないため、

自分でリポジトリから直接 clone して使うか、 node@0.6 系で動かすしか方法が無いです。

おそらく、 socket.io@0.9.8 がリリースされれば治るとは思いますが、

node@0.7 系は unstable だということで、 node@0.6 系の stable の方を重視するような

意味合いのコメントがどっかにありました。そういわれればまあ仕方ないのですが、

とりあえずハマった方々バージョンを見なおしてみてください。

March72012

Socket.IO-Spec を翻訳しました。 #nodejs_jp

Socket.IO と WebSocket が混同されてることある。

Socket.IO は WebSocket の上位プロトコルを定義していて、

それは生の WebSocket とは違う。

で、その仕様はドキュメントとは別、

というか Socket.IO とは別のリポジトリで管理されていて、

以下にある。(たぶん、仕様は別で議論/管理したかったんだと思う。)

https://github.com/LearnBoost/socket.io-spec

で、これあまり表に出てないけど、結構重要だということで翻訳しました。

https://github.com/Jxck/socket.io-spec

これは Socket.IO を正しく理解し、使用するためには必須の知識です。

またを喋れれば、 Node.js じゃないクライアントも書けます。

翻訳ですが、あまり時間がかけられなかったのもあるけど、

なんか微妙に難しかった気が。。

翻訳でも日本語自体でもとにかく issue/pull request 歓迎です。

あと、これ大事だから本体のマニュアルにもう少し近づけてもいいんじゃないかなと思うので、

そのうち本家に相談してみようかなと思ってます。

ちなみに、やってる途中で見つけた typo を本家に pull req したら、

Guille に電光石火でマージされてビビったw 

内容が簡単だとこんなに早いんだな。

March62012

WebSocket が開く未来を、多分開けてない俺達

今日こんなことを聞かれた。

https://twitter.com/#!/bluerabbit777jp/status/176982463040602112

 の考えるWebSocketで今後Webがどうなるか?聞きたいなぁーー

で、まあ素直に

 むしろ俺が聞きたいですw

などと答えてしまったわけですが。。

これじゃあんまりにもあんまりにもなので少しだけ。

ずっと

「WebSocket できる事があまりにも多く、俺達はその可能性を10%も引き出せてないんじゃないかな」

とは漠然と思ってる。

文章を共有することを目的として、ステートレスでプルベースが基本だった Web を、

色んな目的からこれでもかってくらいいじって使いたおしてきたんだけど、

ステートフルでプッシュ可能な技術を手に入れて、

「これであれもできるし、これもできるし、世界変わるぜ!!」って走りだした割には、

チャットとかゲームとか、すでに出尽くしちゃったアイデアから離れられていない感。

個人的には管理系ツールあたりは美味しさがわかりやすいかなというのがあるので、

そのへんのツールは作ってみようかと思ってる。(tail -f ってリアルタイムだし)

リアルタイムで言えば、Wave とか個人的にはとても重要だったと思う。

あれも結局みんなあまり「使いこなせてなかった」よね。

逆にもう少し Wave が続けば、そこで色々試して遊んで

「あーリアルタイムだと、こんなこと出来るな」みたいな

発見や学習ができたと思うんだよね。

Wave を途中で止まらなければ、 Web の進化はもう少しはやかったかもね。

(みんなのパラダイムシフトを後押ししたかもって意味で)

足りてないのがアイデアなのか、技術者が実はパラダイムシフトできていないだけなのか、

そもそもそんなものみんなそこまで必要としてなかったのか。

あんまりよくわからないけど、このままだと「なんだかんだ使いこなせない時代」は少し続きそうな気がする。

まあ、でも Flash とかでは Push もずっとできてたんだから、期待しすぎなのかな。

あー、っていうか「その使いこなす」って何だよってことを聞かれてた気がしてきた。

使いこなせてないんでよくわかりません。

February122012

Lion で ab コマンドがエラー

(やる場合は一回最後まで読んでからの方がいいです。)

Lion に上げた mac で ab(apache bench) を打ったらなんか上手く動かなかった。

$ab -n 10 -c 4 http://localhost:3000/
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

ググったら、ようするにビルドし直せって言われた。

http://stackoverflow.com/questions/7938869/ab-is-erroring-out-with-apr-socket-recv-connection-refused-61#8825278

他に特に出てこなかったので、この通りやる。

ab は apache に付属してるものなので、まず apache のソースを落とし、解凍して configure まで。

$wget wget http://mirrors.kahuki.com/apache//httpd/httpd-2.3.16-beta.tar.bz2
$tar jxvf httpd-2.3.16-beta.tar.bz2 
$cd httpd-2.3.16
$./configure

エラーが出た。

configure: error: pcre-config for libpcre not found. PCRE is required and available from http://pcre.org/

PCRE(Perl Compatible Regular Expressions) が無いから入れろと。

http://pcre.org/ から 8.21 が最新だったので落として入れる。

/usr/local/lib/libpcre 周りにインストールされるようで、そこだけ sudo がいる。

$wget ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/pcre-8.21.tar.bz2
$tar zxvf pcre-8.21.tar.bz2
$cd pcre-8.21.tar.bz2
$./configure
$make
$sudo make install

httpd-2.3.16-beta に戻って configure のやり直し。

無事終わった。

で、こっから普通に make すると apache がビルドされるんだと思うけど、

欲しいのは ab だけで、それは support の中にあるらしい。

$cd support
$make

するとそこに ab コマンドがビルドされる。

試しに使ってみる。

$./ab -n 10 -c 4 http://localhost:3000/
This is ApacheBench, Version 2.3 <$Revision: 1178079 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking localhost (be patient)...bind: Address family not supported by protocol family (47)

なんか localhost が通らない。 ちなみに 127.0.0.1 なら通る。

ググった。

http://www.gossamer-threads.com/lists/apache/users/406323

結局 127.0.0.1 でやれと。解決になってない。。

まあ、とりあえず ab コマンド自体は動いてるし、困らないと言えば困らないし、面倒なのでここまでで辞めた。

自分はやらなかったけど、古い ab と置き換えたい人は、ビルドした support/ab を

which ab で出たファイルと置き換えれば良いと思います。

(自分の MBA は /usr/sbin/ab だった)

December282011

Stream.io の example として Stream だけの Chat #nodejs_jp

Stream.io の example として Stream だけで Chat を実装してみた。

https://github.com/Jxck/stream.io/tree/master/example/chat

いずれも socket.io と jquery

そして、自分が移植した events.js, stream.js util.js を使ってる。

まず server 側では client を Stream に抽象化する。

この時は、 echo サーバであれば、以下のようにつなぐだけ。

io.sockets.on('connection', function(socket) {
  var client = new ClientStream(socket);
  client.resume();
  client.pipe(client);
});

client 側では、 server と input, output をそれぞれ抽象化してしまう。

ここでの server は DuplexStream だ。

$(function() {
  var server = new ServerStream(io);
  var input = new InputStream($);
  var output = new OutputStream($);

  server.resume();
  input.resume();

  input.pipe(server);
  server.pipe(output);
});

あと、もう一つのサンプルはサーバで、でかいファイルを読み込んで

行単位にして送りつけるもの。

ここでは、サーバは fs で作った readableStream を、

「行単位に区切る」という readLineFilter という filter  を通してから、

client につないでいる。 filter はミドルウエアの位置づけになる。

io.sockets.on('connection', function(socket) {
  var readable = fs.createReadStream('sample.log', {encoding: 'utf-8'})
    , readline = new ReadLineFilter()
    , client = new ClientStream(socket)
    ;
  readable.resume();
  readable.pipe(readline).pipe(client);
});

クライアント側では、受け取って表示するだけ。

$(function() {
  var server = new ServerStream(io);
  var output = new OutputStream($);
  server.resume();
  server.pipe(output);
});

今まで面倒だった、イベントの取り回しが、

server - client,  input - output などの粒度で分離して抽象化できて、

しかもそれが pipe() による配管工事だけでアプリを構成できている。

ここから、抽象度を高めて Stream の生成をもっと手軽にし、

Socket.IO とイベント名はその中に隠してしまおうと思ってる。

あとは、 Stream を適切にテストできる仕組みが必要かな。

Stream はインタフェースだから、いずれもそこまで難しくないはず。

https://github.com/Jxck/stream.io/tree/master/template

Stream.io に手応えが出てきた。

December262011

Node.js の assert, events, stream を移植しました。 #nodejs_jp

Stream.IO (https://github.com/Jxck/stream.io) のために、

クライアントで Stream を使いたかったので移植しました。

そのため、依存する events と、テストのための assert をも一緒に移植しました。

  • events は [https://github.com/Wolfy87/EventEmitter]
  • assert は [http://chaijs.com/]

などもあるんですが、

events や stream の Node.js のテストまでは動かなかったので、

そのものまるごと移植しました。

process.on(‘exit’) をブラウザでどう扱ったらいいかよくわからなかったので、

無視したテストも実はありますが、

これでブラウザでも、 Node.js  と同じコードで asset, event, stream が使えます。

これでやっと Stream.IO のクライアント側が実装できる。

November112011

Node.js の環境管理(version manager) について。 #nodejs_jp

[追記] nvm は .zshrc のいじり具合によっては、表示がうまくいかなかったり、すこし変な動きをしたりすることが有るという意味で。nvm 自体はそれらを事細かに対応するつもりはなく、自分は bash に切り替えることで全ての不具合を回避しているという意味です。「nvm は使い物にならん」とか「インストールもまともに出来ない」などと言うつもりはないし、書いてません。実際今使っています。

全ては自分が zsh を使っていることに起因している。

node.js v0.6 のリリースと npm のアップデートも有り、

一旦環境を見直そうと .nvm/ .npm/ を全部消した。

折角ならツールも見直してみようかと思ったが、あまり変わってなかった。

node.js の環境管理(version manager)は以下のものがある。

  • nvm
  • nave
  • n
  • nodeenv

nvm(https://github.com/creationix/nvm)

今まで自分が使っていたものである。

単一の shellでできてて、バージョンごとに npm も入れてくれる。

.nvm/ 以下にビルドしてパスを通してくれる感じ。基本 sudo もいらない。

ということで、結構使いやすい。

がしかし、author の creationix の shell が zsh との相性がすこぶる悪い。

nvm + zsh のトラブルはググればいっぱい出てくるだろう。

issue/pull req も数ある。

しかし、 creationix は以下の方針。

https://github.com/creationix/nvm/wiki/NVM-Forks

「自分の使う機能しか付けない。欲しければ fork しろ。」

結果 fork が 47 乱立してる状態。

自分は nvm install の時だけ bash を上げて使ってた。

nave(https://github.com/isaacs/nave)

isaac 謹製なんだけど、これは node へのパスを通した子shellを起動する。

デフォルト shell の設定をしないと zsh を使ってても bash が開いたり。

そもそも子shellでっていうのが微妙なときも正直ある。

なんで彼はそうしたんだろ、なんかちょっと微妙な気がするけど、

unix 精神を重んじる彼がそうしたなら、きっとこれにはちゃんと理屈が有るんだろうけどよくわからない。

n(https://github.com/visionmedia/n)

みかねて? TJ が作ったのがこれ。

My own flavour of node binary management, no subshells, no profile setup, no convoluted api, just simple.

その名の通りのものなんだけど、

これはインストール先が /usr/local/n 以下にがんがん入る感じ。

個人的には node は何度も入れたり消したりするし、あまり /usr 以下に入れたく無いと思ってる。

~/.n 以下に入れてパスを通すくらいがいいんだけど。

あとnodeを複数入れたときはnコマンド経由らしい。

$ n use 0.3.3 some.js

なんかそれもどうよ。。

nodeenv(https://github.com/ekalinin/nodeenv)

今時こういうツールはpythonあたりで書くのも多いと思う。

そうすればzshとbashの差を気にしたりしないで済む。

もちろん環境に最初からpythonが入ってるとは限らないけど、

自分はmacだから問題ない。

こういうのは歓迎。

$ . env-4.3/bin/activate

(env-4.3)$ npm install express

(env-4.3)$ npm install jade

(env-4.3)$ freeze ../prod-requirements.txt

完全に virtual-env だな。。

色々機能はありそうだけど、なんというか複数のバージョンが簡単に切り替えられれば良くて、

そんなに手厚い API 要らない気がした。

あと最初に pip で入れる時そのための virtualenv とかなんとかなって実際は色々手をかけてしまいそう。

もっと単純で良いや。。

結局 nvm を bash で使って、 .zshrc には nvm use 0.6.1 って書いておくくらいがちょうど良いということで、

また nvm に戻りましたとさ。

October272011

Node.js の Stream

node には readableStream と writableStream というインタフェースがある。

EventEmitter のインスタンスなので、イベントとメソッドをもつ。

何を持つべきかは、マニュアル参照。

そして、node ではしばしばこの Stream を用いてデータのやり取りをすることがある。

ファイルを読むときも、 Socket を使うときも、標準入出力にも stream がでてきて、 受け取ったり、渡したり、 pipe で中継したりして、データの流れをコントロールするような感じ。これは Node では非常に重要な考え方。

たとえばファイルの中身を Stream で読むと、 on(‘data’, cb) でちょっとづつ中身がとれたりする感じ。これだと、一行ごとに処理とかできないので、例えば data イベントのたびに、改行を探して自分で切ってあげたりしないといけない。

このモジュールはそういうことを簡単にできるライブラリのようです。

https://github.com/dominictarr/event-stream

「Stream を極める物は Node を極める」とだれかが言ってました。

October142011

Hook.IO と位置透過性の歴史

Hook.IO というプロダクトがある。

Nodejitsu という実力派スタートアップの Marak が中心になって作られたようで、Node.js のプロダクト。

Node.js はイベントを扱うために、 EventEmitter という仕組みが有るんだけど、このイベントは発生したのと同じプロセス上でしか捕捉できない。

しかし、 Hook.IO はあるプロセスで発生したイベントを他のプロセスで捕捉できる。裏ではネットワーク通信してるので、イベントがスケールするイメージ。

これを使うと、サービスをコンポーネントで分けて、別々のプロセスで起動して、イベントでつなぎ、スケールさせるみたいな構成が可能になる。

非常におもしろいし、これから必要になるだろう仕組みだと思う。

Forever といい、 Node-http-proxy といい、 Nodejitsu はいつも玄人向けのプロダクトを出してくるなぁと思う。

この話をしたら、一つのキーワードを教えてもらった。それが分散アーキテクチャが追い続けた「位置透過性」の歴史。

例えば、ローカルにあるインスタンスも、ネットワークの先にあるインスタンスも、全て同じように扱う事ができる。といった Java の EJB などで追い求められていたこの位置透過性。

しかし、歴史的にその挑戦は割と失敗に終わったものが多いらしい。原因は、どんなに同等に扱えるようにしようとも、分散させた、ネットワークの先のインスタンスの応答は、ローカルより遅いし、だからってインスタンスの場所を意識しないといけないなら、参照透過性のコンセプト自体が破綻する。

その様子を椎名林檎のになぞらえるとこんな感じらしい。

http://kakutani.com/20050731.html#p01

あなたはすぐに分散をさせたがる

あたしは何時も其れを厭がるの

だってリモートになっちゃえば 呼び出しが遅くなるじゃない

あなたはすぐに透過性などと云う

あたしは何時も其れを厭がるの

だって開発者が意識してしまっちゃえば 其れすら嘘になるじゃない

don’ U θink? i 罠 B wiθ U

此処に居て

ずっと

明日のことは判らない

だから同一プロセスにしていてね ダーリン

あなたはすぐにブロックを渡して見せたがる

あたしは何時も其れを喜ぶの

だってファウラーみたいだから あたしがシンディじゃない

Hook.IO はある種歴史を繰り返しているのかもしれないけど、自分はその歴史の一周目をあまり知らないから、何とも言えない。

でも、こういう仕組み(Hook.IO)があって、

その(Marak達の)取り組みに意味があって、

Java が積み上げてきた歴史からも学べることがある。

そういうことを改めて実感した。

ただ、歴史的なものもふまえつつ、たとえ二週目でも価値のある挑戦なら、して行きたいとは思うよね。なんとなく。

 なにより一番驚いたのが、「ギブス(勝訴ストリップ)」が出たのが 11 年も前だと言うことだ。

October92011

パスワードをハッシュにすりゃいいのか。

こんな簡単なことに今まで気がついてなかった自分を、心より恥じる。

例えば、なんかサーバがあって簡単な認証をしたいとき。DBを使うほどでもないし、ファイルで良いし、まあ最悪頑張ってこじ開けられてもそんなに問題じゃない、でも一応 admin パスワードだけ欲しいな。な時。

if(req.body.pass == 'password') //ok

みたいに直には書けない。(さすがにソース見るとばれるのは。。)

で、外部ファイルにして読み込み、 .gitignore に加えてリポジトリにはコミットしない。などする。もしくは環境変数や pit 的なものに入れてそれを見たりする。

// configファイル的なものや環境変数
var config = { pass: 'password'}
var config = // config ファイル的なものか環境変数を読み込む
if(req.body.pass == config.pass) // ok

でも、そうすると本番サーバにはローカルに入って同じファイルや環境変数を作らないと、パスワードが読み込めなくて当たり前のようにエラる。

ログインしたりしないと面倒だし、デプロイだけで動かしたい。

そんな時は、開き直ってパスワードをファイルに書いて、コミット&デプロイする。ただしハッシュにしておけば安心。

node の場合は crypto を使う。こんな感じで一旦自分のパスワードのハッシュを知っておく。

require('crypto').createHash('sha1').update('password').digest('hex');
// '5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8'

それをファイルに書く。

// configファイル的なものや環境変数
var config = { pass: '5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8' }

ログインしたパスワードをハッシュにして認証する。

function cryp(pass) {
  return crypto.createHash('sha1').update(pass).digest('hex');
}
if(cryp(req.body.pass) === config.pass) // ok

これなら、 .gitignore に入れる事も、ファイルがなかったりする事も、 ssh ログインすることも pit もいらない。こんな簡単な事を今まで思いつかなかった。ちゃんと DB を用意するか、 .gitignore やらを使ってた。やれやれだぜ。。

← Older Entries Page 1 of 5