ObjectSquare [2011 年 3 月号]

[技術講座]


Node.js - サーバーサイド Javascript フレームワーク


OGIS International, Inc. 山野 裕司


Node.js、そしてサーバサイド Javascript

Node.js はオープンソースのサーバーサイド Javascript アプリケーションフレームワークです。シングルスレッドのイベントループモデルとノンブロッキングI/Oが特徴です。Javascript のエンジンには高速なGoogleのV8を使っています。

Javascript というと Web ブラウザ用のプログラミング言語だと思いがちですが、実は 15 年以上前にも Netscape 社のアプリケーションサーバはサーバサイドの Javascript をサポートしていました。ただし、クライアントサイドも含めて、それほど普及しませんでした。2005 年ごろの Ajax の登場により Javascript 自体が見直され、Web アプリケーションにとって必須のプログラミング言語となったなか、2009 年に Node.js が生まれました。2010 年にはクラウドホスティングサービスを提供しているJoyent社Node.js ベースのサービスのベータ版を開始しました。Joyent 社は Node.js プロジェクト自体にも貢献しています。Joyent 社以外では、HP webOS 2.0もNode.js を採用しています。

Node.js のメーリングリストや Node.js 用のライブラリやフレームワークの開発は非常に活発で、開発者からも大きく注目されていることがわかります。 Node.js 以外では、ErbixというRhinoをJavascript エンジンとして使った Javascript アプリケーションプラットフォーム(PaaS)も登場しています。このような動向を考えると、今後、サーバサイド Javascript は大きなトレンドになるのではないかと思われます。

なぜ Node.js なのか

なぜ、Node.js は開発者から注目されているのでしょうか。 理由は 2 つあると思われます。

最初の理由は Javascript です。 Web アプリケーションを作る場合には Javascript を避けて通るわけにはいきません。 したがって、開発者がクライアント側だけでなく、サーバサイドのコードも Javascript で書きたくなるのは自然でしょう。

もう一つの理由は、シングルスレッドのイベントループモデルとノンブロッキング I/O という Node.js のアーキテクチャです。 このアーキテクチャのおかげで、デッドロックや意図しないデータの書き換えが発生しない安全、かつ高速なプログラムを簡単に書くことができます。 そして、これは WebSocket を使ったリアルタイム Web アプリケーションと相性が良いのです。 また、シングルスレッドであるため、大量のトランザクション発生時のメモリ使用量を抑えることもできます。

このような特徴から、例えば、エンタープライズ向けの Twitter といえる Yammer 社、リアルタイムの広告の分析をおこなっている AdGear 社のようなリアルタイム性が重要なアプリケーションで Node.js は使われています。 ただし、Node.js はフロントエンドだけに向いているわけではありません。 例えば、研究者のコミュニティサイトである Academia.edu やモバイル用の音楽共有サイトである Chompin は、バックエンドのシステムにも Node.js を使用しています。

Node.js入門

では、Node.js のコードを見ていきましょう。 これ以降のコードや手順は、Mac OS X 上で Node.js 0.3.1 を使い動作を確認しています。 Node.js は対話型の評価環境を提供しています。 例えば、シェル上で以下のようにプログラムを実行することができます。 % はシェルのプロンプト、> は Node.js のプロンプトです。

% node
> console.log("hello, world\n")
hello, world

node コマンドの引数にスクリプトファイル名を渡すと、そのスクリプトが実行されます。

% cat hello.js 
console.log("hello, world\n")
% node hello.js 
hello, world

node コマンドを --help オプション付きで起動すると、指定可能なオプションや環境変数に関する情報が表示されます。

% node --help
Usage: node [options] script.js [arguments] 
Options:
  -v, --version        print node's version
  --debug[=port]       enable remote debugging via given TCP port
                       without stopping the execution
  --debug-brk[=port]   as above, but break in script.js and
                       wait for remote debugger to connect
  --v8-options         print v8 command line options
  --vars               print various compiled-in variables
  --max-stack-size=val set max v8 stack size (bytes)

Enviromental variables:
NODE_PATH              ':'-separated list of directories
                       prefixed to the module search path,
                       require.paths.
NODE_DEBUG             Print additional debugging output.
NODE_MODULE_CONTEXTS   Set to 1 to load modules in their own
                       global contexts.
NODE_DISABLE_COLORS  Set to 1 to disable colors in the REPL

Documentation can be found at https://nodejs.org/api.html or with 'man node'

では、簡単な echo サーバを見てみましょう。

var net = require('net');
net.createServer(function (socket) {
    socket.on("data", function (data) {
          console.log(socket.remoteAddress + " " + data);
	  socket.write(data);
    });
}).listen(8007, "127.0.0.1");

プログラムの最後が liseten() で終わっているのに注目してください。 クライアントからの入力を書き戻すのは、socket.on() に渡されているクロージャです。 そして、このクロージャは、クライアントからの入力を受け取った時、つまり data イベントが発生した時に Node.js によって自動的に呼ばれます。 これが、Node.js のイベントループモデルの特徴的な部分です。 では、実行してみましょう。 適当な文字、この場合は Hey! Ho! Let's Go! を入力すると、その文字がそのままエコーバックされます。

% telnet localhost 8007
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Hey! Ho! Let's Go!
Hey! Ho! Let's Go!

もう少し複雑な例を見てみましょう。 これは 5 秒ごとに Twitter 上の Node.js に関する話題を検索するクライアントです。 request.on() で HTTP リクエストに対する response イベントに対するコールバックを、response.on() で HTTP レスポンスに対する data イベントと end イベントに対するコールバック関数を設定しています。 イベントの種類や関数に関しては、node(1) の man page か、Node.js の API ドキュメントを参照してください。 ここで興味深いのは setTimeout() です。 この関数によって、5 秒後に関数 search 自身が呼ばれ続けます。

var http = require('http');
var headers = {"Host": "search.twitter.com", 
               "User-Agent": "NodeJS HTTP Client"};
var since = 0;
var con = http.createClient(80, "search.twitter.com");

function search() {
    var path = "/search.json?q=nodejs" + "&since_id="+ since;
    var request = con.request('GET', path, headers);
    request.end();
    request.on('response', function (response) {
      var body = "";
      response.setEncoding("utf8");
      response.on('data', function (chunk) { body += chunk; });
      response.on('end', function () {
          var tweets = JSON.parse(body);
          var results = tweets["results"];
          for (var i = results.length - 1; i >= 0; i--) {
              if (results[i].id > since) { since = results[i].id; }
              console.log("@" + results[i].from_user + ": " + results[i].text);
          }
      });
    });
    setTimeout(search, 5000);
};
 
search();

では実行してみましょう。

% node twitter.js
@nodejitsu: RT @LegNeato: Whoops, looks like node failed on https://pulse.mozilla.org/live overnight. Now using forever (https://bit.ly/eQCY8b). Check it out!
@ishiduca: ブログ書いた「Mongoseのmodelメソッドで定義したcollection名について」 https://blog.livedoor.jp/ishiduca/archives/51154090.html #MongoDB #nodejs #mongoose #javascript
@nsyee: おっと、まだリリース出てないようだけどnave use latestしたらv0.3.2が落ちてきた。tlsモジュールが追加されてる。 #nodejs_jp
@masahiroh: RT @nsyee: おっと、まだリリース出てないようだけどnave use latestしたらv0.3.2が落ちてきた。tlsモジュールが追加されてる。 #nodejs_jp
@mattcodes: @FransBouma Im writing a lot in NodeJS at moment, but I am conscious that I need 2 adopt something more versatile too, probably python.
@liangzan: @zhenyitan nodejs libs are not as mature as ruby/rails yet. choose ruby for a quick turn around

これらの例を見ればわかると思いますが、Node.js 自体は Web アプリケーションフレームワークというわけではありません。 ただし、Express のような Node.js 上で動作する Web フレームワークは存在していますし、MySQLのドライバー や NoSQL の一種である MongoDB用 のドライバーなども存在します。

まとめ

Node.js はオープンソースのサーバーサイド Javascript アプリケーションフレームワークで、シングルスレッドのイベントループモデルとノンブロッキング I/O が特徴です。 これらの特徴のおかげで、安全で高速なプログラムを簡単に書くことができます。 Node.js に限らず、サーバーサイド Javascript は注目を集めており、これからの大きなトレンドの 1 つになるのではないかと思われます。



© 2011 OGIS-RI Co., Ltd.
Index
Index