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

Like this? Share it with your network

Share

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

on

  • 26,801 views

 

Statistics

Views

Total Views
26,801
Views on SlideShare
20,473
Embed Views
6,328

Actions

Likes
53
Downloads
94
Comments
0

26 Embeds 6,328

http://d.hatena.ne.jp 5388
http://tech.nifty.co.jp 443
http://localhost 179
https://twitter.com 132
https://sonic.qiita.com 60
http://10.1.0.27 29
http://us-w1.rockmelt.com 29
http://ama-ch.github.io 25
http://favtile.com 8
http://feedly.com 6
http://tweetedtimes.com 4
http://webcache.googleusercontent.com 4
http://baido.info 4
http://digg.com 2
http://hatenatunnel.appspot.com 2
http://pinterest.com 2
http://mundo-powerpoints.blogspot.com.es 2
http://dev1.atosaku.com 1
http://newsblur.com 1
http://nuevospowerpoints.blogspot.com 1
http://nuevospowerpoints.blogspot.mx 1
http://cloud.feedly.com 1
http://www.google.com 1
http://www.twylah.com 1
http://b.hatena.ne.jp 1
http://reader.softama.com 1
More...

Accessibility

Categories

Upload Details

Uploaded via as Microsoft PowerPoint

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

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

  • 1. そうだったのか! よくわかる process.nextTick() Node.jsのイベントループを理解する IIJ 大津 繁樹 (@jovi0608) 2012年6月28日 東京Node学園6時限目
  • 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. 今日の話• Nodeのイベントループとは• process.nextTickとは• node-devでの大論争• 今後どうなる?• process.nextTickの正しい使 い方おそらく世界初?のNode-v0.8ベースでイベントループを解説(libuvの大幅な変更に追随)(注: 説明はLinuxが対象です。)
  • 4. Nodeのイベントループとは、• Node の心臓 イベントループが終了したら Node は死にます。
  • 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. イベントループが回り続けるには アクティブな handle/req がなけれ ば イベントループが終了https://github.com/joyent/node/blob/v0.8.0-release/deps/uv/src/unix/core.c#L252-261
  • 7. handle と req の違い• handle – I/O が発生してない時でもイベントループを 維持 – (例) server.listen()• req – I/Oが発生している時だけイベントループを 維持 – (例) http.get()
  • 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. 実際のコードでは、(その1)var http = require(http); アクティブ ハンドルvar server = http.createServer(); 0 アクティブハンドルが無いからNode終了
  • 10. 実際のコードでは、(その2)var http = require(http);var server = http.createServer(); アクティブserver.listen(1234); ハンドル追加 (+1) アクティブハンドルが作成されNode は終了しない。実際は epoll wait (Linux)して
  • 11. 実際のコードでは、(その3)var http = require(http);var server = http.createServer();server.listen(1234, function() { アクティブ server.close(); ハンドル削除 (+1-1=0)});アクティブハンドルがすぐ無効化されるのでNode終了
  • 12. イベントループの中身 7つのステップ 1. 時刻更新 2. タイマー実行 3. アイドル実行 4. Prepare実行 5. I/Oイベント実行 (libev) 6. Check実行 7. ハンドル終了
  • 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. イベントループを止めてはいけない! 終わり 始まり 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. なぜ3カ所も nextTick() があるの?
  • 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. 理由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. 理由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. 理由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. 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. 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. node-devでの大論争 推進派 擁護派• 今までの動作がそもそもおか • 別のAPIにすればいいじゃない しかった。正しい動作に変え か るだけ • 実際にコード変更するのがど• CPU処理の分散のために再帰を んなに大変か 使うのは悪いこと、child • どうせ今さら何言っても聞き process を使え 入れてくれないだろう• idle用リスナの用途に再帰を使 うのはわからんでもないが、 setTimeoutを使え• API名を変えるのはもう遅い• 実際にI/Oの取りこぼしでバグ が出ている。この変更でそれ を直すのが優先する
  • 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. 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. 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. 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. まとめ• Node のイベントループの仕組みを良く理解 した上でイベントループを止めないことを意 識してコードを書きましょう。• process.nextTick() は、 – 非同期イベントの発生 – 非同期コールバックの実行 の用途で使いましょう。• CPUを消費する処理には、child process を利 用しましょう。• node-v0.9 では process.nextTick()の動作仕様 が変わる予定です。