Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
そうだったのか! よくわかる   process.nextTick() Node.jsのイベントループを理解する   IIJ 大津 繁樹 (@jovi0608)        2012年6月28日     東京Node学園6時限目
Nodeの歩み(参考)2007/10 libev公開           2010/08 nodejs_jp開始2008/05 libeio公開          2010/09 no.de開始2009/09 Google V8公開      ...
今日の話•   Nodeのイベントループとは•   process.nextTickとは•   node-devでの大論争•   今後どうなる?•   process.nextTickの正しい使    い方おそらく世界初?のNode-v0.8ベ...
Nodeのイベントループとは、• Node の心臓  イベントループが終了したら  Node は死にます。
Nodeのイベントループの正体  Node が起動する時に uv_run() が呼  ばれます。(src/node.cc:2910)https://github.com/joyent/node/blob/v0.8.0-release/deps/...
イベントループが回り続けるには                              アクティブな handle/req がなけれ                                       ば               ...
handle と req の違い• handle  – I/O が発生してない時でもイベントループを    維持  – (例) server.listen()• req  – I/Oが発生している時だけイベントループを    維持  – (例)...
handle と req の種類              handle                       reqASYNC        非同期ジョブの操作      CONNECT       stream接続CHECK     ...
実際のコードでは、(その1)var http = require(http);         アクティブ                                     ハンドルvar server = http.createServ...
実際のコードでは、(その2)var http = require(http);var server = http.createServer();                                アクティブserver.listen...
実際のコードでは、(その3)var http = require(http);var server = http.createServer();server.listen(1234, function() {                  ...
イベントループの中身  7つのステップ      1. 時刻更新      2. タイマー実行      3. アイドル実行      4. Prepare実行      5. I/Oイベント実行         (libev)      6....
Node-v0.8イベントループ概要                             終わり     始まり                                                    setTimeout()...
イベントループを止めてはいけない!                              終わり    始まり                                                    setTimeout() ...
なぜ3カ所も nextTick() があるの?
理由1:呼び出し順番setTimeout(function(){  console.log(‘3:foo’);                                    $ node tick-order.js}, 0);     ...
理由1:呼び出し順番                         終わり     始まり                                                  setTimeout()              ...
理由2:入れ子の呼び出し順番process.nextTick(function() { setTimeout(function(){    console.log(‘4:foo);                                ...
理由2:入れ子の呼び出し順番                            終わり      始まりconsole.log(‘3:hoge’)                                 setTimeout()  ...
process.nextTick()の説明(マニュアルより)    イベントループの次以降のループでコールバッ    クを呼び出します。 これは setTimeout(fn, 0) の    単純なエイリアスではなく、 はるかに効率的で    ...
node-v0.9に向けて isaacs からの提案• process.nextTick()でイベントハンドラを追加するのはよくやること  だけど  次のイベントループでハンドラが登録されるまでの間にイベントが  発生したりするとI/Oの取りこ...
node-devでの大論争      推進派                  擁護派• 今までの動作がそもそもおか      • 別のAPIにすればいいじゃない  しかった。正しい動作に変え        か  るだけ            ...
今後どうなるのか(想像)                                   終わり     始まり                                                          setTim...
process.nextTickの正しい使い方var events = require(events);var util = require(util);                 非同期イfunction Hoge() {       ...
process.nextTickの正しい使い方var events = require(events);var util = require(util);function Hoge(cb) {                         非...
process.nextTickの再帰を避けるvar cluster = require(cluster);if (cluster.isMaster) {                  CPU消費処理は  var worker = clus...
まとめ• Node のイベントループの仕組みを良く理解  した上でイベントループを止めないことを意  識してコードを書きましょう。• process.nextTick() は、 – 非同期イベントの発生 – 非同期コールバックの実行 の用途で使...
そうだったのか! よくわかる process.nextTick() node.jsのイベントループを理解する
Upcoming SlideShare
Loading in …5
×

そうだったのか! よくわかる process.nextTick() node.jsのイベントループを理解する

34,867 views

Published on

  • Be the first to comment

そうだったのか! よくわかる process.nextTick() node.jsのイベントループを理解する

  1. 1. そうだったのか! よくわかる process.nextTick() Node.jsのイベントループを理解する IIJ 大津 繁樹 (@jovi0608) 2012年6月28日 東京Node学園6時限目
  2. 2. Nodeの歩み(参考)2007/10 libev公開 2010/08 nodejs_jp開始2008/05 libeio公開 2010/09 no.de開始2009/09 Google V8公開 2010/11 Joyent管轄へ2009/02 ry Node開発開始 2011/02 node-v0.4.0リリース2009/05 node-v0.0.1リリース 2011/03 東京Node学園#12009/06 nodejs ML開始 2011/10 東京Node学園祭2009/10 npm公開 2011/11 node-v0.6.0リリース2009/11 JSConf EU ry発表 2011/12 Azureサポート2010/04 Herokuサポート 2012/01 isaacs管理へ2010/08 node-v0.2.0リリース 2012/06 node-v0.8.0リリース
  3. 3. 今日の話• Nodeのイベントループとは• process.nextTickとは• node-devでの大論争• 今後どうなる?• process.nextTickの正しい使 い方おそらく世界初?のNode-v0.8ベースでイベントループを解説(libuvの大幅な変更に追随)(注: 説明はLinuxが対象です。)
  4. 4. Nodeのイベントループとは、• Node の心臓 イベントループが終了したら Node は死にます。
  5. 5. Nodeのイベントループの正体 Node が起動する時に uv_run() が呼 ばれます。(src/node.cc:2910)https://github.com/joyent/node/blob/v0.8.0-release/deps/uv/src/unix/core.c#L265
  6. 6. イベントループが回り続けるには アクティブな handle/req がなけれ ば イベントループが終了https://github.com/joyent/node/blob/v0.8.0-release/deps/uv/src/unix/core.c#L252-261
  7. 7. handle と req の違い• handle – I/O が発生してない時でもイベントループを 維持 – (例) server.listen()• req – I/Oが発生している時だけイベントループを 維持 – (例) http.get()
  8. 8. handle と req の種類 handle reqASYNC 非同期ジョブの操作 CONNECT stream接続CHECK ループの最後の操作 WRITE stream書き込みFS_EVENT ファイルイベント操作 SHUTDOWN stream停止FS_POLL statの問い合わせ操作 UDP_SEND udp 送信IDLE アイドルの時の操作 FS ファイル操作NAMED_PIPE 名前付きパイプの操作 WORK ワーカスレッドPOLL fdイベントの操作 GETADDRINFO アドレス情報取得PREPARE ループの最初の操作PROCESS プロセスの操作TCP TCPの操作 後で見てTIMER タイマー操作 おいて下TTYUDP TTYPの操作 UDPの操作 さい。
  9. 9. 実際のコードでは、(その1)var http = require(http); アクティブ ハンドルvar server = http.createServer(); 0 アクティブハンドルが無いからNode終了
  10. 10. 実際のコードでは、(その2)var http = require(http);var server = http.createServer(); アクティブserver.listen(1234); ハンドル追加 (+1) アクティブハンドルが作成されNode は終了しない。実際は epoll wait (Linux)して
  11. 11. 実際のコードでは、(その3)var http = require(http);var server = http.createServer();server.listen(1234, function() { アクティブ server.close(); ハンドル削除 (+1-1=0)});アクティブハンドルがすぐ無効化されるのでNode終了
  12. 12. イベントループの中身 7つのステップ 1. 時刻更新 2. タイマー実行 3. アイドル実行 4. Prepare実行 5. I/Oイベント実行 (libev) 6. Check実行 7. ハンドル終了
  13. 13. Node-v0.8イベントループ概要 終わり 始まり setTimeout()nextTick() 1:時刻更新 7:ハンドル終了 6:run_check 2:run_timersコールバッ イベントループ nextTick() ク 一周(Tick) ユーザ 5:poll 3:run_idle プログラム 4:run_prepare libev+kernel epoll: Linux nextTick() kqueue: BSD event port: Solaris (注: ユーザプログラムは 3: run_idle から始まる
  14. 14. イベントループを止めてはいけない! 終わり 始まり setTimeout() nextTick() 1:時刻更新 7:ハンドル終了 6:run_check 2:run_timers こんなコードはダメ! while(1) コールバッ ク while(1) { console.log(‘hoge’); 5:poll } 3:run_idle ずっとここ libev+kernel で止まる! 4:run_prepare epoll: Linux kqueue: BSD nextTick() event port: Solaris (注: ユーザプログラムは 3: run_idle から始まる。
  15. 15. なぜ3カ所も nextTick() があるの?
  16. 16. 理由1:呼び出し順番setTimeout(function(){ console.log(‘3:foo’); $ node tick-order.js}, 0); 1:piyoprocess.nextTick(function() { 2:hoge console.log(‘2:hoge’); 3:foo});console.log(‘1:piyo’);setTimeout() より process.nextTick() が先に呼ばれる (注: 将来仕様が変わる可能性があります。)
  17. 17. 理由1:呼び出し順番 終わり 始まり setTimeout() console.log(‘3:foo’) nextTick() 1:時刻更新 7:ハンドル終了 6:run_check 2:run_timers console.log(‘1:piyo’) コールバッ イベントループ nextTick() ク 一周(Tick) 5:poll 3:run_idle 4:run_prepare console.log(‘2:hoge’) nextTick()(注: ユーザプログラムは 3: run_idle から始まる。)
  18. 18. 理由2:入れ子の呼び出し順番process.nextTick(function() { setTimeout(function(){ console.log(‘4:foo); $ node tick-order2.js }, 0); 1:piyo process.nextTick(function() { 2:bar console.log(‘3:hoge); 3:hoge }); 4:foo console.log(‘2:bar);});console.log(‘1:piyo’);process.nextTick() のスコープ内でも setTimeout()より process.nextTick() が先に呼ばれる (注: 将来仕様が変わる可能性があります。)
  19. 19. 理由2:入れ子の呼び出し順番 終わり 始まりconsole.log(‘3:hoge’) setTimeout() console.log(‘4:foo’) nextTick() 1:時刻更新 7:ハンドル終了 6:run_check 2:run_timers console.log(‘1:piyo’) コールバッ イベントループ nextTick() ク 一周(Tick) 5:poll 3:run_idle 4:run_prepare console.log(‘2:bar’) nextTick()(注: ユーザプログラムは3: run_idleから始まる。)
  20. 20. process.nextTick()の説明(マニュアルより) イベントループの次以降のループでコールバッ クを呼び出します。 これは setTimeout(fn, 0) の 単純なエイリアスではなく、 はるかに効率的で す。for (var i = 0; i < 1024*1024; i++) { 処理時間 process.nextTick(function (){ Math.sqrt(i); } ); 0.360u 0.072s 0:00.44 97.7%} 約5倍の差for (var i = 0; i < 1024 * 1024; i++) { setTimeout(function () { Math.sqrt(i) }, 0); 1.700u 0.800s 0:02.51 99.6%} おそらくリンクリストの生成と時刻取得のオーバヘッドによるものだろう(未
  21. 21. node-v0.9に向けて isaacs からの提案• process.nextTick()でイベントハンドラを追加するのはよくやること だけど 次のイベントループでハンドラが登録されるまでの間にイベントが 発生したりするとI/Oの取りこぼしが起きてしまう。• 次のイベントが発生する前に確実にハンドラを登録をするために、 V8でJSを実行した直後に process.nextTick() に登録された関数を全部 実行するようにしたい。• 再帰処理とかの展開もそこで行うので次のようなコードでは setTimeout() は起動しなくなるよ。 setTimeout(function() { console.log(timeout); }, 1000); process.nextTick(function f() { process.nextTick(f); });
  22. 22. node-devでの大論争 推進派 擁護派• 今までの動作がそもそもおか • 別のAPIにすればいいじゃない しかった。正しい動作に変え か るだけ • 実際にコード変更するのがど• CPU処理の分散のために再帰を んなに大変か 使うのは悪いこと、child • どうせ今さら何言っても聞き process を使え 入れてくれないだろう• idle用リスナの用途に再帰を使 うのはわからんでもないが、 setTimeoutを使え• API名を変えるのはもう遅い• 実際にI/Oの取りこぼしでバグ が出ている。この変更でそれ を直すのが優先する
  23. 23. 今後どうなるのか(想像) 終わり 始まり setTimeout() 1:時刻更新 7:ハンドル終了 nextTick()nextTick() 2:run_timers 全展開 全展開 6:run_check コールバッ イベントループ ク 一周(Tick) 5:poll 3:run_idle 4:run_prepare libev+kernel epoll: Linux kqueue: BSD event port: Solaris 再帰は一定回数繰り返したら遅延させるかも
  24. 24. process.nextTickの正しい使い方var events = require(events);var util = require(util); 非同期イfunction Hoge() { ベントの var self = this; 生成 process.nextTick(function() { self.emit(foo); });}util.inherits(Hoge, events.EventEmitter);var hoge = new Hoge();hoge.on(foo, function() { console.log(foo event emitted);});
  25. 25. process.nextTickの正しい使い方var events = require(events);var util = require(util);function Hoge(cb) { 非同期コール if(cb) { バックの呼び出 process.nextTick(function() { cb(); し }); }}util.inherits(Hoge, events.EventEmitter);Hoge.prototype.setfoo = function(arg) { this.foo = arg;};var hoge = new Hoge(function() { hoge.setfoo(bar); console.log(hoge.foo);});
  26. 26. process.nextTickの再帰を避けるvar cluster = require(cluster);if (cluster.isMaster) { CPU消費処理は var worker = cluster.fork(); 子プロセスで worker.on(message, function(msg) { console.log(msg); });} else { //子プロセス while(1) { process.send(‘hoge’); }}
  27. 27. まとめ• Node のイベントループの仕組みを良く理解 した上でイベントループを止めないことを意 識してコードを書きましょう。• process.nextTick() は、 – 非同期イベントの発生 – 非同期コールバックの実行 の用途で使いましょう。• CPUを消費する処理には、child process を利 用しましょう。• node-v0.9 では process.nextTick()の動作仕様 が変わる予定です。

×