2013/11/26更新

[NodeJS] nodeで例外処理を書いて、最低限落ちないサーバー実装を行うException Handler

このエントリーをはてなブックマークに追加            

こんにちは、@yoheiMuneです。
最近、node.jsを使ってサーバー運用を始めてみました。
初めてのことが多くて大変ですが、その分学んだこともたくさんあるので、ブログにドシドシとアウトプットしたいと思います。

画像



node.jsでは1つのエラーでサーバーダウン。。

今まで最終的なエラーハンドリングは、ApacheやらSpringやらstrutsやらに任せてきた自分としては、node.jsは斬新です。 1つのJSエラーが発生するとサーバーが停止してしまいます。
例えば、以下のようなサーバーコードがあったとして、エラー部分が実行されるとサーバーは応答しなくなります。
// 必要なモジュールを読み込みます。
var http = require("http");
var fs = require("fs");

var server = http.createServer(function (req, res) {
	res.writeHead(200, {"Content-Type":"text/html"});
	var output = fs.readFileSync("./index.html", "utf-8");
	res.end(output);
});
server.listen(8080);

undefinedFunction();  // 定義していない関数の呼び出しでエラー!!!
上記の場合、例えば以下のようなエラーが表示されて、サーバーは停止します。
$ node app.js 

/Users/munesadayohei/tmp/app.js:13
undefinedFunction();  // 定義していない関数の呼び出しでエラー!!!
^
ReferenceError: undefinedFunction is not defined
    at Object.<anonymous> (/Users/munesadayohei/tmp/app.js:13:1)
    at Module._compile (module.js:456:26)
    at Object.Module._extensions..js (module.js:474:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Function.Module.runMain (module.js:497:10)
    at startup (node.js:119:16)
    at node.js:901:3
また、以下のように、「/」にアクセスした時に実行されるメソッドに不備があり、サーバーが停止することもあります。
// 必要なモジュールを読み込みます。
var http = require("http");
var fs = require("fs");

var server = http.createServer(function (req, res) {
	res.writeHead(200, {"Content-Type":"text/html"});
	var output = fs.readFileSync("./index.html", "utf-8");

	// JSONパースができずエラー
	var json = JSON.parse('<tag>NOT JSON FORMART</tag>');

	res.end(output);
});
server.listen(8080);
「/」にアクセスすると、以下のようなエラーが発生して、サーバーは停止します。
$ node app.js

undefined:1
<tag>NOT JSON FORMART</tag>
^
SyntaxError: Unexpected token <
    at Object.parse (native)
    at Server.<anonymous> (/Users/munesadayohei/tmp/app.js:10:18)
    at Server.EventEmitter.emit (events.js:98:17)
    at HTTPParser.parser.onIncoming (http.js:2027:12)
    at HTTPParser.parserOnHeadersComplete [as onHeadersComplete] (http.js:119:23)
    at Socket.socket.ondata (http.js:1917:22)
    at TCP.onread (net.js:510:27)
と、こんな感じでnode.jsを利用すると、1つのエラーがサーバーが停止してしまうので、なかなかスリリングです。
通常「関数の未定義」みたいな初歩的なエラーはテストで発見できます。 しかし「予期せぬ入力値」などはテストケースから漏れることもあり、またエラーケースに対する実装が十分でないこともあるので、 運用中にも予期せぬエラーが発生することはぜひ想定すべきです。

Apacheなどのサーバーを利用していたり、フレームワーク(strutsやSpringとか)を使っている場合には、 こーゆうエラーもいい感じに処理してくれるのですが、node.jsでサーバーを実装するともちろんしてくれないので、大変ですねー。



node.jsでとりあえず落ちないサーバーを作る

とりあえず落ちないようにするために、Exception Handlerを実装します。 node.jsのネイティブでサポートしている仕組みで、サーバー実装の最初の方に以下ように記載します。
// 必要なモジュールを読み込みます。
var http = require("http");
var fs = require("fs");

// ★★ここがポイントです★★
// サーバー実装の前に、エラーハンドリングを記載します。
process.on('uncaughtException', function(err) {
    console.log(err);
});

var server = http.createServer(function (req, res) {
	res.writeHead(200, {"Content-Type":"text/html"});
	var output = fs.readFileSync("./index.html", "utf-8");

	// 以下のエラーが発生しても、とりあえずサーバーは生き残ります。
	// JSONパースができずエラー
	// var json = JSON.parse('<tag>NOT JSON FORMART</tag>');

	res.end(output);
});
server.listen(8080);

// ここのエラーでも生き残ります。
// undefinedFunction();
上記のようにuncaughtExceptionイベントを実装することで、エラーが発生してもそのメソッド内で対応して、サーバーが落ちないようにできます。 ただ、uncaughtExceptionでキャッチした場合には、どのリクエストのものかは分からないので、エラーレスポンスを返すことはできません。

エラーが発生するかもという箇所は、ちゃんとエラーハンドリングを個別に書くのがベストです。
// ちゃんとエラー処理を書く
try {
	var json = JSON.parse('<tag>NOT JSON FORMART</tag>');
} catch (e) {
	res.writeHead(400, {"Content-Type":"text/html"});
	res.end('invalid format');
	return;
}



最後に

node.jsを使って初めて本格的なサーバー運用を行ってますが、こんな基本的なことからいっぱい学ぶ今日この頃です。 node.jsはまだまだ楽しみなところが多くていいですね。今後もノウハウをブログに書きたいと思います。

最後までご覧頂きましてありがとうございました。





こんな記事もいかがですか?

RSS画像

もしご興味をお持ち頂けましたら、ぜひRSSへの登録をお願い致します。