Submit Search
そうだったのか! よくわかる process.nextTick() node.jsのイベントループを理解する
•
Download as PPTX, PDF
•
80 likes
•
28,300 views
S
shigeki_ohtsu
Follow
1 of 28
Download now
Downloaded 119 times
More Related Content
そうだったのか! よくわかる process.nextTick() node.jsのイベントループを理解する
1.
そうだったのか! よくわかる
process.nextTick() Node.jsのイベントループを理解する IIJ 大津 繁樹 (@jovi0608) 2012年6月28日 東京Node学園6時限目
3.
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学園#1 2009/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リリース
4.
今日の話 •
Nodeのイベントループとは • process.nextTickとは • node-devでの大論争 • 今後どうなる? • process.nextTickの正しい使 い方 おそらく世界初?の Node-v0.8ベースでイベント ループを解説 (libuvの大幅な変更に追随) (注: 説明はLinuxが対象です。)
5.
Nodeのイベントループとは、 • Node の心臓
イベントループが終了したら Node は死にます。
6.
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
7.
イベントループが回り続けるには
アクティブな handle/req がなけれ ば イベントループが終了 https://github.com/joyent/node/blob/v0.8.0-release/deps/uv/src/unix/core.c#L252-261
8.
handle と req
の違い • handle – I/O が発生してない時でもイベントループを 維持 – (例) server.listen() • req – I/Oが発生している時だけイベントループを 維持 – (例) http.get()
9.
handle と req
の種類 handle req ASYNC 非同期ジョブの操作 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 タイマー操作 おいて下 TTY UDP TTYPの操作 UDPの操作 さい。
10.
実際のコードでは、(その1) var http =
require('http'); アクティブ ハンドル var server = http.createServer(); 0 アクティブハンドルが無いからNode終了
11.
実際のコードでは、(その2) var http =
require('http'); var server = http.createServer(); アクティブ server.listen(1234); ハンドル追加 (+1) アクティブハンドルが作成されNode は終了しない。実際は epoll wait (Linux)して
12.
実際のコードでは、(その3) var http =
require('http'); var server = http.createServer(); server.listen(1234, function() { アクティブ server.close(); ハンドル削除 (+1-1=0) }); アクティブハンドルがすぐ無効化される のでNode終了
13.
イベントループの中身 7つのステップ
1. 時刻更新 2. タイマー実行 3. アイドル実行 4. Prepare実行 5. I/Oイベント実行 (libev) 6. Check実行 7. ハンドル終了
14.
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 から始まる
15.
イベントループを止めてはいけない!
終わり 始まり 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 から始まる。
16.
なぜ3カ所も nextTick() があるの?
17.
理由1:呼び出し順番 setTimeout(function(){ console.log(‘3:foo’);
$ node tick-order.js }, 0); 1:piyo process.nextTick(function() { 2:hoge console.log(‘2:hoge’); 3:foo }); console.log(‘1:piyo’); setTimeout() より process.nextTick() が先に呼ばれる (注: 将来仕様が変わる可能性があります。)
18.
理由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 から始まる。)
19.
理由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() が先に呼ばれる (注: 将来仕様が変わる可能性があります。)
20.
理由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から始まる。)
21.
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% } おそらくリンクリストの生成と時刻取得のオーバヘッドによるものだろう(未
22.
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); });
23.
node-devでの大論争
推進派 擁護派 • 今までの動作がそもそもおか • 別のAPIにすればいいじゃない しかった。正しい動作に変え か るだけ • 実際にコード変更するのがど • CPU処理の分散のために再帰を んなに大変か 使うのは悪いこと、child • どうせ今さら何言っても聞き process を使え 入れてくれないだろう • idle用リスナの用途に再帰を使 うのはわからんでもないが、 setTimeoutを使え • API名を変えるのはもう遅い • 実際にI/Oの取りこぼしでバグ が出ている。この変更でそれ を直すのが優先する
24.
今後どうなるのか(想像)
終わり 始まり 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 再帰は一定回数繰り返したら遅延させるかも
25.
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'); });
26.
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); });
27.
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’); } }
28.
まとめ • Node のイベントループの仕組みを良く理解
した上でイベントループを止めないことを意 識してコードを書きましょう。 • process.nextTick() は、 – 非同期イベントの発生 – 非同期コールバックの実行 の用途で使いましょう。 • CPUを消費する処理には、child process を利 用しましょう。 • node-v0.9 では process.nextTick()の動作仕様 が変わる予定です。
Download