nodejs
差分
このページの2つのバージョン間の差分を表示します。
| 両方とも前のリビジョン前のリビジョン次のリビジョン | 前のリビジョン | ||
| nodejs [2011/10/24 18:04] – nullpon | nodejs [2016/10/13 02:30] (現在) – [--max-old-space-size] nullpon | ||
|---|---|---|---|
| 行 1: | 行 1: | ||
| ====== node.js ====== | ====== node.js ====== | ||
| - | [[http:// | + | [[http:// |
| - | node.jsはCommonJSというサーバサイドJavaScriptの仕様にしたがっている。 | + | ===== バージョンとES feature ===== |
| - | ===== HTML5 の Server-Sent Events を実装してみる ===== | + | ブラウザと違って互換性を気にする必要はないので最新機能は遠慮なく使いましょう |
| - | [[http:// | + | node v4 |
| - | サーバ側を node.js で実装してみる。 | + | * デフォルト引数、分割代入、import以外のES2015機能が使えます。 |
| + | |||
| + | node v6 | ||
| + | |||
| + | * import以外のES2015機能が使えます。 | ||
| + | |||
| + | ===== callbackに関するtips ===== | ||
| + | |||
| + | ==== callbackの第一引数はerrにする ==== | ||
| + | |||
| + | 絶対にエラーの発生しない処理だとしてもcallbackの第一引数はエラーが渡される想定のコードにすること。第一引数にnullを渡してcallbackを呼べし。 | ||
| + | |||
| + | node, npmの多くのライブラリは第一引数でエラーを受け取る事を前提にしているので、こうすることで各種ライブラリ、モジュールとスムーズに連携できるようになる。 | ||
| - | generator.js | ||
| <code javascript> | <code javascript> | ||
| - | // EventEmitterを取得 | + | // これはだめ |
| - | var events = require(" | + | function asyncFunc(callback) { |
| - | var emitter = new events.EventEmitter(); | + | setImmediate(function() { |
| + | callback(true); | ||
| + | }); | ||
| + | } | ||
| + | </ | ||
| - | // EventEmitterを拡張 | + | <code javascript> |
| - | emitter.id = 0; | + | // OK |
| - | emitter.data = ""; | + | function |
| - | emitter.start = function() { | + | setImmediate(function() { |
| - | var chars = " | + | |
| - | var self = this; | + | |
| - | setInterval(function() { | + | } |
| - | var pos = Math.floor(Math.random() * chars.length * 2); | + | </ |
| - | if (pos < chars.length) { | + | |
| - | self.id++; | + | |
| - | self.data = chars.charAt(pos); | + | |
| - | // generatedイベントとして登録されたリスナを呼び出す | + | |
| - | self.emit(" | + | |
| - | } | + | |
| - | }, 1000); | + | |
| - | }; | + | |
| - | // module.exportsに設定されたオブジェクトがrequireの戻り値となる。 | + | |
| - | module.exports = emitter; | + | ==== 非同期処理には必ずcallbackを ==== |
| + | |||
| + | 呼び出し元に結果を通知する必要のない非同期処理でも必ずcallbackを付ける(PromiseかEvent駆動ならcallbackなし可) | ||
| + | |||
| + | bad | ||
| + | |||
| + | <code javascript> | ||
| + | function writeLogToDB(code, | ||
| + | // 失敗しても良い処理なので、呼び出しもとにエラーを通知しなくてもいい (← ダメです。だいたい後で通知したくなります) | ||
| + | db.Log.insert({_id: | ||
| + | err && mainLogger.error(err); | ||
| + | }); | ||
| + | } | ||
| </ | </ | ||
| - | server.js | + | good |
| <code javascript> | <code javascript> | ||
| - | var http = require(" | + | function writeLogToDB(code, |
| - | http.createServer(handler).listen(8124); | + | // callbackが渡されてなければデフォルトの処理を入れる |
| + | if (!callback) callback | ||
| + | err && mainLogger.error(err); | ||
| + | } | ||
| + | |||
| + | | ||
| + | callback(err); | ||
| + | }); | ||
| + | } | ||
| + | </ | ||
| - | var generator | + | ==== callbackを2度呼ばない ==== |
| - | generator.start(); | + | |
| - | function | + | < |
| + | function | ||
| + | // 途中省略... | ||
| + | |||
| + | try { | ||
| + | | ||
| + | } catch (e) | ||
| + | | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | try側のcallback中に例外が発生するとcatch側のcallbackが呼ばれるので2回呼ばれてしまう。これによってデータ2重書き込み等の重大な問題が発生する恐れがあります。 | ||
| + | |||
| + | |||
| + | ===== require ===== | ||
| + | |||
| + | ==== require時に処理を走らせない ==== | ||
| + | |||
| + | requireされるファイルは関数やクラスの定義のみにする。読み込んだだけでファイルの書き込み読み込み、サーバの起動、DB接続その他の処理を行ってはいけない。 | ||
| + | |||
| + | Good | ||
| + | <code javascript> | ||
| + | // hoge.js | ||
| + | var hoge = module.exports; | ||
| - | | + | hoge.init = function() { |
| - | | + | // 何か初期化処理 |
| - | + | ||
| - | res.writeHead(200, | + | |
| - | " | + | |
| - | }); | + | |
| - | res.write(" | + | |
| - | res.write(" | + | |
| - | res.write(" | + | |
| - | res.end(); | + | |
| - | + | ||
| - | } | + | |
| - | generator.on(" | + | |
| } | } | ||
| + | |||
| + | |||
| + | // main.js | ||
| + | var hoge = require(" | ||
| + | hoge.init(); | ||
| </ | </ | ||
| - | クライアント側のJavaScript | + | Bad |
| <code javascript> | <code javascript> | ||
| - | var source | + | // hoge.js |
| - | source.onmessage | + | var hoge = module.exports; |
| - | // データ受信時の処理を書く | + | |
| - | // event.data でサーバから送られてきたデータを取得できる | + | hoge.init = function() { |
| + | // 何か初期化処理 | ||
| } | } | ||
| + | |||
| + | hoge.init(); | ||
| + | |||
| + | |||
| + | // main.js | ||
| + | require(" | ||
| </ | </ | ||
| + | requireされるだけで何らかの処理が走るようにすると、起動時に予期せぬタイミングでinitが実行されたり、テストコードでinitが不要なのに呼ばれてしまったりする問題が発生します。 | ||
| + | |||
| + | ===== メモリ ===== | ||
| + | |||
| + | ==== --max-old-space-size ==== | ||
| + | < | ||
| + | デフォルトで1.5GB以上のメモリを確保しようとするとクラッシュします。ひたすらメモリを食いつぶしていくコードを実行すると… | ||
| + | |||
| + | ``` | ||
| + | <--- Last few GCs ---> | ||
| + | |||
| + | 84313 ms: Mark-sweep 1389.5 (1456.5) -> 1396.1 (1456.5) MB, 844.5 / 0 ms [allocation failure] [GC in old space requested]. | ||
| + | 85167 ms: Mark-sweep 1396.1 (1456.5) -> 1398.0 (1456.5) MB, 854.0 / 0 ms [allocation failure] [GC in old space requested]. | ||
| + | 86028 ms: Mark-sweep 1398.0 (1456.5) -> 1387.1 (1456.5) MB, 860.4 / 0 ms [last resort gc]. | ||
| + | 86898 ms: Mark-sweep 1387.1 (1456.5) -> 1389.0 (1456.5) MB, 870.4 / 0 ms [last resort gc]. | ||
| + | |||
| + | |||
| + | <--- JS stacktrace ---> | ||
| + | |||
| + | ==== JS stack trace ========================================= | ||
| + | |||
| + | Security context: 0x2e7afd4b4629 <JS Object> | ||
| + | 1: nextTick [node.js: | ||
| + | 2: arguments adaptor frame: 5->1 | ||
| + | 3: onwrite(aka onwrite) [_stream_writable.js: | ||
| + | |||
| + | FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - process out of memory | ||
| + | zsh: abort node hoge.js | ||
| + | ``` | ||
| + | |||
| + | 上限を増やすには | ||
| + | |||
| + | ``` | ||
| + | $ node --max-old-space-size=2048 hoge.js | ||
| + | ``` | ||
| + | |||
| + | これを実行すると… | ||
| + | |||
| + | ``` | ||
| + | <--- Last few GCs ---> | ||
| + | |||
| + | 183271 ms: Scavenge 2044.5 (2114.7) -> 2044.5 (2114.7) MB, 0.8 / 0 ms (+ 1.4 ms in 1 steps since last GC) [allocation failure] [incremental marking delaying mark-sweep]. | ||
| + | 184563 ms: Mark-sweep 2044.5 (2114.7) -> 2035.4 (2114.7) MB, 1291.3 / 0 ms (+ 2.3 ms in 2 steps since start of marking, biggest step 1.4 ms) [last resort gc]. | ||
| + | 185914 ms: Mark-sweep 2035.4 (2114.7) -> 2036.5 (2114.7) MB, 1351.8 / 0 ms [last resort gc]. | ||
| + | |||
| + | |||
| + | <--- JS stacktrace ---> | ||
| + | |||
| + | ==== JS stack trace ========================================= | ||
| + | |||
| + | Security context: 0x1f9ab28b4629 <JS Object> | ||
| + | 1: $toString(aka ToString) [native runtime.js: | ||
| + | 2: a(aka a) [/ | ||
| + | 3: /* anonymous */ [/ | ||
| + | |||
| + | FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - process out of memory | ||
| + | zsh: abort node --max-old-space-size=2048 hoge.js | ||
| + | ``` | ||
| + | |||
| + | 今度は2GBまで持ちました。が、こんなにメモリを使う実装の前に色々直しましょう | ||
| + | </ | ||
nodejs.1319479461.txt.gz · 最終更新: by nullpon