Node.jsとCommonJSについて

日経ソフトウェア8月号にJavaScriptの特集がありまして、そこでNode.jsが紹介されていました。
それを読んで、僕は以下のようなツイートしました。

*1だと言わざるをえません。Node.jsはCommonJSの仕様のうち「Module 1.0」と「Unit Testing 1.0」には一応準拠していることになっています(http://wiki.commonjs.org/wiki/CommonJS#Implementations)。が、Node.jsのその他のAPI(たとえばfsモジュールのwrite()など)はCommonJSに従った形で実装されているわけではありません。

さらに準拠していることになっている「Module 1.0」にしても、CommonJSで規定されているモジュールの作り方は

exports.hoge = function() {...};

なのですが、Node.jsではそれに加えて以下の2通りでもモジュールを作ることができます。

module.exports.hoge = function() {...};
this.hoge = function() {...};

よって、Node.jsにはCommonJSにない独自仕様が存在していると言えます。
また「Unit Testing 1.0」にしても、CommonJSで規定されているAssertとTestのモジュールのうち、Node.jsにあるのはAssertモジュールだけなので、実際には準拠しているとは言いがたい状態です。

結局、Node.jsがCommonJSの一部仕様に準拠していると主張しているのは(上のWikiがそうであるように)CommonJS側であって、Node.js側ではそのような主張は(昔はしていたこともありましたが)積極的にはしていません。公式サイトAPIドキュメントにもそのような記述はないはずです(GithubのWikiには一部残ってるかもしれませんが)。

さらに先日Node.jsの作者のRyan DahlがRedditに降臨して「Node.jsの作者だけど何か質問ある?」スレを立てたときに、「CommonJSはもうオワコンだから、今後NodeのAPI変えるときにはCommonJSのことなんて一切気にしないよ」宣言がありました。

以上のように、Node.jsとしてはすでにCommonJSのことは眼中にないといった感じです。なので、今後Node.jsをCommonJSと絡めて論ずるときには十分注意を払った上で行った方がよろしいと思います。

*1:「嘘」っていうのはちょっときつい表現だったかもしれませんね。すみません。140字ギリギリだったので(言い訳)。

Ubuntu 11.04 Natty Narwhal に Node と npm をインストール

nvmを使う

$ sudo apt-get install git curl build-essential libssl-dev
$ git clone https://github.com/creationix/nvm.git ~/.nvm
$ . ~/.nvm/nvm.sh
$ nvm install v0.4.7

". ~/.nvm/nvm.sh" は .bashrc などに記述しておくと吉
以後、Nodeを使うときには

$ nvm use latest

を実行する

naveを使う

$ sudo apt-get install git curl build-essential libssl-dev
$ git clone https://github.com/isaacs/nave.git ~/.nave
$ ~/.nave/nave.sh use latest
$ curl http://npmjs.org/install.sh | sh

"~/.nave/nave.sh" へのシンボリックリンクをパスの通ったところに置いておくと吉
以後、Nodeを使うときには

$ nave use latest

を実行する

ツールを使わずに

$ sudo apt-get install git curl build-essential libssl-dev
$ curl http://nodejs.org/dist/node-v0.4.7.tar.gz | tar xz
$ cd node-v0.4.7
$ ./configure --prefix=~/local
$ make 
$ make install

~/local/bin にパスを通す

$ curl http://npmjs.org/install.sh | sh

「東京Node学園 1時限目」が無事に終わりました

開催にあたってご尽力頂いた各位にお礼申し上げます。

資料等は http://blog.nodejs.jp/2011/03/node-1_25.html にまとめていますので是非ご覧ください。
ただし、なんか最新のChromeで見るとところどころ文字化けしてるように見える……。

イベント中にもお伝えしましたが、Nodeに対する興味がより高い方を優先して参加していただけるよう今後のイベントはまずNode.js日本ユーザグループMLに流し、その後Twitter等でオープンにするという形にします。

ですので、今後もNode.js日本ユーザグループ主催のイベントに参加したいという方は、是非MLに参加してください。

Amazon EC2 MicroインスタンスのAmazon LinuxにNodeをインストールした

ついに東京にやってきたので。

AMI(Amazon Machine Image)は基本となるBasic 64-bit Amazon Linux AMI 2011.11.1 Beta (AMI Id: ami-9008a391)を選びました。UbuntuとかSUSEとかもあるから好きなの選べばいいんだけど、Ubuntuなら既にインストールの情報多いからあえて違うので。

とりあえず、SSHでログインできるようになってからの作業ログ。

# 時計をJSTに
$ sudo cp /usr/share/zoneinfo/Japan /etc/localtime
# 必要なパッケージのインストール
$ sudo yum install git gcc-c++ openssl-devel
# nvmの準備
$ git clone git://github.com/creationix/nvm.git ~/.nvm
$ echo . ~/.nvm/nvm.sh >> ~/.bashrc
$ . ~/.bashrc
# nvmを使ってNodeをインストール
$ nvm install v0.4.2

これでNode v0.4.2と最新のnpmがインストールされます。あとは、

$ nvm use v0.4.2

とすれば node コマンドや npm コマンドにパスが通るので使えるようになります。

makeに時間はかかったけど、とりあえずプロセスが殺されるようなことはありませんでした。

jsdo.itで動くWebSocketのチャットをはてダに貼ってみる

jsdo.itのブログパーツが貼れるらしいので試しに。

WebSocket(Socket.IO)を使ったチャットサンプル - jsdo.it - share JavaScript, HTML5 and CSS

サーバサイドはもちろんNodeです。
リアルタイムWebハッカソンでハンズオンしてきました - 自分の感受性くらいで作成したチャットとサーバは共同なので、http://riajyu.mesolabs.com/でも同じログが表示されるのが確認出来ると思います。

認証等は皆無なのでTwitter IDに適当に他人のIDを入力すると簡単に成りすましができます。あと、アイコンの表示にはid:dankogaiTwitterアイコンAPIを勝手に使わせてもらっています。

サーバサイドの実装も含めてソースはGithubで公開していますが、Nodeがまだv0.2.xの時代に作ったものなのでちょっと書き方が古いとこもあります(とはいえ最新のv0.4.1でもそのままで動いてますが)。

ATNDのリマインダーをNodeで書いてみた

Node.jsの正式名称はNodeということらしいので、これからはNodeと表記しますね。

今日、「Node.jsの飲み会」というイベントをやるんですが、信頼のドタキャン率を誇るATNDで募集していたので、ATNDのアカウントをTwitterとひもづけてる人に対してTwitter経由でリマインダーを送るプログラムをNodeで書いてみました。

var request = require('request'),
    qs = require('querystring'),
    OAuth = require('oauth').OAuth,
    settings = require('./settings'),
    oauth = new OAuth(
      'https://api.twitter.com/oauth/request_token',
      'https://api.twitter.com/oauth/access_token',
      settings.oauth.consumer_key,
      settings.oauth.consumer_secret,
      '1.0a',
      null,
      'HMAC-SHA1'
    );

function getUsers(eventId, callback) {
  var params = {
    format: 'json',
    event_id: eventId
  }
  var url = 'http://api.atnd.org/events/users/?' + qs.stringify(params);

  request({uri: url}, function(err, res, body) {
    if (err) throw err;
    if (res.statusCode !== 200) throw new Error('ATND API returns NG');
    
    var event = JSON.parse(body).events[0];
    event.users.forEach(function(element, index, array) {
      callback(event, element);
    });
  });
}

function update(message) {
  oauth.post(
    'http://api.twitter.com/1/statuses/update.json',
    settings.oauth.access_token,
    settings.oauth.access_token_secret,
    { status: message },
    function(err, data) {
      err && console.log(err);
    }
  );
}

getUsers(13366, function(event, user) {
  if (user.status === 1 && user.twitter_id) {
    update('@' + user.twitter_id + ' 今日は「' + event.title + '」の日です!あなたは参加人数にカウントされておりますので、もし参加できない場合は今すぐキャンセルをお願いします。お会いできるのを楽しみにしています!' + event.event_url);
  }
});

これをapp.jsという名前で保存します。同じディレクトリにsettings.jsという名前で

module.exports.oauth = {
  consumer_key:         'your consumer key here',
  consumer_secret:      'your consumer secret here',
  access_token:         'your access token here',
  access_token_secret:  'your access token secret here'
}

という内容のファイル(それぞれのキーはTwitterAPIのページで取得してね)を作成して、あとは

$ node app.js

で、リマインダーをスパムのようにつぶやきます。

Github: https://github.com/meso/ATND-Reminder
もし要望があれば、これにWebインタフェースつけてこういったリマインダーを送るサービスでも作ろうかしら。

Node.jsについてのよくある誤解

Node.jsは普及しそうに無い: ニュースの社会科学的な裏側

http://anlyznews.blogspot.com/2011/02/nodejs.html

という記事があったので、もしかしたらいい感じにNode.jsについてのよくある誤解がここに網羅されてるんじゃないかと思ってエントリを書きます。決して顔真っ赤にして反論してるわけじゃないよ!

で、まず

理由は簡単で、Googleがリリースしたアプリケーション・サーバーであるnode.jsが、JavaScriptとその実行エンジンを基本とした構成となっており、その性能が高いからだ。

とありますが、Node.jsはGoogle製ではありません。Node.jsがベースとしているJavaScriptのV8エンジンはGoogle製(のオープンソース)ですが、Node.js自身はJoyentという会社のサポートによって作られています。メインコミッタのRyan DahlもJoyentの社員です。
修正されてました!

あとは、

9. node.jsの問題点
高負荷時のパフォーマンスが優れているのは確かのようだが、現状では次のような欠点もある。万能薬的な使い方は出来ないようだ。

この項目について書けばいいと思うので各指摘点について書いていきます。っていうか、どのような言語であれどのようなフレームワークであれ得手不得手(もしくは適材適所)というものがあるので、万能薬は存在しないというのは一般的な共通認識ということでいいんじゃないのかな。Node.jsの開発者でNode.jsは万能だと言ってる人は誰一人みたことがないですし。

1. イベントループ・モデルで処理を直列化しているので、あるリクエストの処理に時間がかかる場合、他の全てのリクエストをブロックする可能性がある。

まず、この処理というのがI/Oを伴うものだと仮定すると、Node.jsはI/Oを非同期で行うためブロックは発生しません。他の非同期フレームワークであるTwisted(Python)やEventMachine(Ruby)などでは、組み合わせて使用するその言語のライブラリ次第でブロックが発生してしまうことがありますが、Node.jsは非同期であることが前提の環境なのでライブラリで同期I/Oが使われていてそれを同期呼び出しするようなケースはほぼありません*1
I/Oを伴わない部分だとすると、そのように時間のかかる処理(CPUインテンシブな処理)には向かないと言えます。ただ、一般的なWebアプリケーションであればI/O処理よりもCPU処理に時間のかかるケースというのはほとんどないのではないでしょうか。CPUがL1/L2キャッシュやRAMのみを用いて処理している場合とHDDやネットワークを用いる場合との差は、以下のエントリに端的に表現されています。

このブロックしてしまう処理というのはI/Oに端的に表れていて、たとえばCPUのL1キャッシュだと3サイクル、L2で14、RAMだと250で済むんだけど、それがDISKになると41,000,000サイクルかかって、ネットワークならさらに240,000,000サイクルもかかってしまうんだ。これがどのくらいすごいのかというと、サイクルをメートル換算してみるとわかりやすい。RAMでも250メートルでまだ目の届く範囲なんだけど、DISKになった瞬間、地球一周分、ネットワークだとさらに地球6周分!!!このコストの比はやっぱりトンデモナイんだよね。

http://d.hatena.ne.jp/badatmath/20101020/1287587240

あとはまあ、子プロセス作ってCPUインテンシブな処理はそっちにさせるってのも手ですね。

2. イベントループ・モデルで平行処理を行わないため、複数コアを持つCPU/MPU、複数のCPUを使うSMPでパフォーマンスの上昇が、JavaScriptのコード部分は期待できない。

これに関してもそういう理解をしている人は多いと思いますし、実際にNode.jsを1プロセスだけ立ち上げてる場合ではその通りです。しかし、Node.jsには複数の子プロセスを起動してそれらに処理を分散させるための様々なツール(ライブラリ)が既に存在し、使用実績もあります。例えば、fugue, Spark2, clusterなどです。これらを使えばコアの数だけプロセスを立ち上げ、リクエストをそのプロセスにロードバランスするみたいなことが簡単にできます。セッションの共有とかめんどいんじゃないかって思われるかもしれませんが、ExpressのようなConnectベースのWebアプリケーションであれば、connect-redisを使うなどすれば2行追加するだけでセッションの共有ができるようになります。

3. JavaScriptプログラミング言語としての限界が影響する。つまり、オブジェクト指向が不完全であること、動的型付けでJavaC#に対して速度面に限界があること等が、大規模で複雑なアプリケーションには影響を及ぼす可能性がある。

オブジェクト指向が不完全というあたりはちょっとよく分かりませんね。動的型付けで速度面に限界があることが大規模で複雑なアプリケーションに影響を及ぼすというのもよくわかりません。
どちらかというと、JavaScriptのプロトタイプベースのオブジェクト指向が、JavaC#などのクラスベースのオブジェクト指向しかやってこなかった人にとって慣れるのに時間がかかるとか、そういった問題はあるかとおもいます。また、動的型付けなのでIDEの支援が受けにくいとか多人数による開発であればきちんとルールを決めておかないと混乱を巻き起こす可能性があるとかそういったことはあるでしょう。ただ、前者についてはJavaScriptに慣れてる開発者も多いでしょうし、後者については他の動的言語でも大規模開発の実績があるのだから乗り越えられる障壁なんだと思っています。

4. あるページのJavaScriptの文法エラーが、サーバーの停止を引き起こす。簡単なプログラム・ミスが、アプリケーションの非クリティカルな部分に発生したとしても、システム全体がダウンする結果となる。

これはひどい誤解で、クライアント側の(HTMLから呼ばれる)JavaScriptが間違っていたからといって、サーバ側のNode.jsのプロセスが止まるなんてことはありません。それとも僕が誤読してるのかな?

追記:誤読してたっぽい。あるページの処理を行うサーバ側のJavaScriptでエラーが発生した場合のことだったみたいです。これも下のコメントに書かれているとおり、適切なエラー処理を行っていればNode.jsプロセス全体がダウンするなんてことはありません。

5. 非同期化によるパフォーマンス向上がイベントループ・モデルに依存するため、イベントを受けるコールバック関数が多くなり(単純なファイル操作でも3回コールバックが発生する)、ソースコードの見通しが悪くなる。

これもよくある指摘なんですが、様々な対処方があります。JavaScript自体に関しても、Future(Promise)を使ったパターンやDeferredを使ったパターンなどのライブラリも充実していますし、Node.jsについてもasync.jsなどの使い勝手のいいライブラリが数多く存在します(ちょっと前のエントリだけどhttp://d.hatena.ne.jp/koichik/20100926#1285502400参照)。

6. 利用可能ライブラリが限定的であり、開発支援ツールが無い。Apacheモジュールも種類や用途が多いし、JavaPHPにも覚えきれないほどのフレームワーク製品群とライブラリがある。これらに依存しない用途にしか、現状では利用できない。

利用可能なライブラリは確かに他言語に比べるとまだまだ少ないかもしれませんが、僕の個人的な感想では現状でもあまり不満はありません。また、既存のJavaScriptのライブラリ(例えばjQueryやYahoo!UIなど)は簡単にNode.jsから使うことができます。開発支援ツールという意味では、JavaScriptが使えるIDEやエディタなら大抵普通にコードは書けますし、Node Inspectorを使えばGUI環境でデバッグすることも可能です。ちなみにCloud9というNode.js製のIDEも存在し、これはローカルにインストールせずにホスティング環境で使うこともできます。
JavaPHPの誕生後1年でどれぐらいのライブラリやフレームワークが揃っていたかは知りませんが、生まれたての環境としては十分じゃないかなと思っています。ってか、欲しいのがあれば作ればいいだけだし、逆に言うと作って名を挙げるチャンスがあるとも言えますね。

終わりに

で、まあ上に書いたことは全部今書いてる本にもっと詳しく解説されています。というわけで出版されたら買ってね!という宣伝エントリでした。

*1:100%ではありませんが