• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
そうだったのか! よくわかる process.nextTick() node.jsのイベントループを理解する
 

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

on

  • 25,512 views

 

Statistics

Views

Total Views
25,512
Views on SlideShare
19,357
Embed Views
6,155

Actions

Likes
50
Downloads
94
Comments
0

25 Embeds 6,155

http://d.hatena.ne.jp 5288
http://tech.nifty.co.jp 441
http://localhost 179
https://twitter.com 121
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://baido.info 4
http://webcache.googleusercontent.com 4
http://mundo-powerpoints.blogspot.com.es 2
http://hatenatunnel.appspot.com 2
http://digg.com 2
http://pinterest.com 2
http://cloud.feedly.com 1
http://dev1.atosaku.com 1
http://www.twylah.com 1
http://reader.softama.com 1
http://www.google.com 1
http://nuevospowerpoints.blogspot.com 1
http://nuevospowerpoints.blogspot.mx 1
http://b.hatena.ne.jp 1
http://newsblur.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のイベントループを理解する そうだったのか! よくわかる process.nextTick() node.jsのイベントループを理解する Presentation Transcript

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