Slide 1

Slide 1 text

Nodeコアの歩き方 ~Nodeの開発に参加しよう~ IIJ 大津 繁樹 東京Node学園祭2012 2012年11月18日 こっちは 時間ないかも

Slide 2

Slide 2 text

自己紹介 • 株式会社 インターネットイニシアティブ(IIJ) 所属 • 戦略的開発室というところで Node やら HTML5 やら SPDY なんかをやってます。 • twitter: @jovi0608 • https://github.com/shigeki よろしくです。 こっちも 11月20日(火) 19:00~ @IIJ

Slide 3

Slide 3 text

内容 Nodeコアの中をうろうろ歩きます! (個人的に大事だと思うところを6点) 1. Node の ソース 2. イベントループの話(前々回のアップデート) 3. Node APIの土地勘を持とう! 4. JavaScript と C++ の出会い 5. WRAP! WRAP! WRAP! 6. Node 起動! • Nodeの開発に参加しよう(無理かも)

Slide 4

Slide 4 text

libev c-ares http-parser zlib openssl V8 コアモジュール ユーザモジュール libuv Node.js アーキテクチャ概要(v0.9.3) epoll kqueue event port IOCP npm ネイティブ(JS) ビルトイン(C++) libeio削除 削除 予定

Slide 5

Slide 5 text

1. Node のソース https://github.com/joyent/node # Popular Star Popular Fork 1 bootstrap (40,318) Spoon-Knife (10,441) 2 node (18,626) bootstrap(10,019) 3 jquery (17,532) homebrew (4,948) 4 rails (16,512) rails (4,246) 5 html5-boilerplate html5-boilerplate 6 impress.js hw3_rottenpotatoes 7 backbone oh-my-zsh (2,828) 8 homebrew node (2,753) 9 Spoon-Knife jquery 10 d3 phonegap-start みんなでNodeを Star & Fork しよう!

Slide 6

Slide 6 text

Nodeソースツリー (ルート上のファイル) 情 報 系 AUTHORS 貢献者リスト(名前 、メールアドレス) ChangeLog 変更履歴 LICENSE ライセンス Readme.md README ビ ル ド 系 vcbuild.bat MS Visual Studio向けビルドバッチスクリプト configure 初期設定スクリプト(config.gypi, config.mk を生成) Makefile Unix向けメイクファイル(gmake用) BSDmakefile BSD make向け案内用(gmakeコマンドを使えと表示) node.gyp ビルド用GYPファイル common.gypi GYP共通設定ファイル config.gypi gyp向け初期設定ファイル (configureで生成) config.mk Makefile向け設定ファイル(configureで生成)

Slide 7

Slide 7 text

Nodeソースツリー (ルート上のディレクトリ) benchmark/ ベンチマークスクリプト群 deps/ 外部依存ライブラリ群 doc/ マニュアル等ドキュメント類 lib/ JavaScriptネイティブモジュール群 src/ 本体(node.cc/node.js) と C++ビルドイン モジュール群 test/ テスト tools/ ビルド・インストール用ツール群他 out/ ビルド用ディレクトリ(gypが生成)

Slide 8

Slide 8 text

• C++ で記述 • node_extensions.h 経由で 参照 • libuv やV8等の他のライブ ラリを利用するために使わ れる。 • process.binding()で読み込 み コアモジュールの種類 ビルトインモジュール src/*.cc • JavaScriptで記述 • node_natives.h 中に格納 (後述) • 主にユーザモジュールに提 供するAPIとして使われる。 • module.require()で読み込 み ネイティブモジュール src/node.js, lib/*.js

Slide 9

Slide 9 text

Nodeソースツリー (外部依存ライブラリ群) deps/cares 非同期DNSライブラリ deps/http_parser/ HTTPパーサー deps/npm/ パッケージ管理 deps/openssl/ TLSライブラリ deps/uv/ libuv (イベントライブラリ) deps/v8/ V8 (JavaScriptエンジン) deps/zlib/ 圧縮・解凍ライブラリ

Slide 10

Slide 10 text

libev c-ares http-parser zlib openssl V8 コアモジュール ユーザモジュール libuv epoll kqueue event port IOCP npm ネイティブ(JS) ビルトイン(C++) ここの 話題 2. イベントループの話

Slide 11

Slide 11 text

libuv • Node.js の心臓 、IOイベントを扱うライブラリ • Unix/WinのマルチOSをサポート。統一IF提供 • 非同期な POSIX-like API を用意 – deps/uv/include/uv.h libuv

Slide 12

Slide 12 text

前々回のアップデート http://www.slideshare.net/shigeki_ohtsu/processnext-tick-nodejs

Slide 13

Slide 13 text

Node-v0.9.3イベントループ概要 1:時刻更新 7:ハンドル終了 5:poll 始まり 終わり 4:run_prepare nextTick() 再帰展開 setImmeidate() setTimeout() setInterval() コールバック 3:run_idle イベントループ 一周(Tick) libev+kernel epoll: Linux kqueue: BSD event port: Solaris IOCP: WIn 2:run_timers 6:run_check (注: メインモジュールは 3: run_idle から始まる。) メイン モジュール nextTick() 再帰展開 New libev 削除予定 New New

Slide 14

Slide 14 text

process.nextTick()の再帰計算はダメよ $ ./node ~/fib.js (node) warning: Recursive process.nextTick detected. This will break in the next version of node. Please use setImmediate for recursive deferral. 7.0330367711422765e+208 function fib(i, n1, n2, f) { if (i === 1) { f(n2); } else { process.nextTick(function() { fib(i-1, n2, n1 + n2, f); }); } } fib(1001, 0, 1, function(k) { console.log(k); }); node-v0.9では、 process.maxDepth (default:1000) まで展開 計算をしますが下記警 告が出ます。 setImmediate() を使おう! 去年学園祭でもやったフィボナッチ計算

Slide 15

Slide 15 text

じゃsetImmediate()使ったら function fib(i, n1, n2, f) { if (i === 1) { f(n2); } else { setImmediate(function() { fib(i-1, n2, n1 + n2, f); }); } } fib(1001, 0, 1, function(k) { console.log(k); }); Immediate なのに 1msec 待つバグのPull Request 放置中です orz @isaacs Please check https://github.com/joyent/node/pull/3872 $ ./node ~/fib2.js 7.0330367711422765e+208

Slide 16

Slide 16 text

libev c-ares http-parser zlib openssl V8 コアモジュール ユーザモジュール libuv epoll kqueue event port IOCP npm ネイティブ(JS) ビルトイン(C++) ここの 話題 3. Node APIの土地勘を持とう!

Slide 17

Slide 17 text

クラス図を公開中 https://github.com/shigeki/node-class-diagram 小さくて見えないから次で 概要を。 くら

Slide 18

Slide 18 text

ネイティブモジュールの構造概要 events. EventEmitter stream. Stream net. Server http.Outgoing Message http.Incoming Message tls. CryptStream net. Socket http.Server Response http.Client Request tls. ClearText http.Server Request http.Client Response http. Server tls. Server https. Server on(), emit() pipe() listen(),close() connection event Node APIの 土地勘を持つ

Slide 19

Slide 19 text

Node APIの 土地勘を持つ http/https サーバには 固有メソッド がないね。 connection イベント listen() close() 等は共通だ • connection • secureConnection • connect イベントの違いがわ かるかな?

Slide 20

Slide 20 text

全部同じに書けます (socket生データの出力) // net.Server var net_server = require('net').createServer(); net_server.on('connection', function(socket) { socket.on('data', function(chunk) { console.log('net', chunk.toString()); }); }); net_server.listen(8080); // tls.Server var tls_server = require('tls').createServer(opts); tls_server.on('connection', function(socket) { socket.on('data', function(chunk) { console.log('tls', chunk.toString()); }); }); tls_server.listen(8081); // http.Server var http_server = require('http').createServer(); http_server.on('connection', function(socket) { socket.on('data', function(chunk) { console.log('http', chunk.toString()); }); }); http_server.listen(8082); // https.Server var https_server = require('https').createServer(opts); https_server.on('connection', function(socket) { socket.on('data', function(chunk) { console.log('https', chunk.toString()); }); }); https_server.listen(8083); Node APIの 土地勘を持つ

Slide 21

Slide 21 text

Node APIの 土地勘を持つ サーバ レスポンス クライアント リクエスト ヘッダ関連APIや 書き込み・終了メソッド は共通しているよね。

Slide 22

Slide 22 text

Node APIの 土地勘を持つ サーバ リクエスト クライアント レスポンス なんと!この2つは同じオブジェクト プロパティ SeverRequest ClientResponse method ○ × url ○ × connection ○ × statusCode × ○

Slide 23

Slide 23 text

この2つは一緒だよ // http.ClientResponse var client = require('http').get('http://example.jp', function(res) { for( var key in res) { console.log('client', key); } }); // http.ServerRequest var server = require('http').createServer(function(req, res) { for( var key in req) { console.log('server', key); } res.setHeader(200, {‘content-type’, ‘text/plain’}); res.end('Hello World'); }).listen(8080); Node APIの 土地勘を持つ

Slide 24

Slide 24 text

libev c-ares http-parser zlib openssl V8 コアモジュール ユーザモジュール libuv epoll kqueue event port IOCP npm ネイティブ(JS) ビルトイン(C++) ここの 話題 4. JavaScript と C++ の出会い

Slide 25

Slide 25 text

JavaScript と C++ の出会い C++ JavaScript ビルド編、呼び出し編、実行編

Slide 26

Slide 26 text

JavaScript と C++の出会い (ビルド編: js2c) Native Module src/node.js lib/*.js out/Release/obj/gen/ node_natives.h ASCIIコードの配列 tools/js2c.py DefineJavaScript() src/node_javascript.cc 参照 via gyp

Slide 27

Slide 27 text

process.binding() BuiltinModule C++ src/node_extensions.cc module.require() NativeModule JavaScript src/node_natives.h src/node_javascript.cc User Module JavaScript JavaScript と C++の出会い (呼び出し編:process.binding) process.binding()

Slide 28

Slide 28 text

JavaScript と C++の出会い (実行編: WRAP) V8 JavaScript C++ WRAP HandleWrap::HandleWrap(Handle object, uv_handle_t* h) { if (h) { h->data = this; } HandleScope scope; object_ = v8::Persistent::New(object); object_->SetPointerInInternalField(0, this); } src/handle_wrap.cc C++のインスタンスをV8 の JavaScript オブジェクトの中に包み隠しています。 V8オブジェクトはクラスの プライベー トメンバーに格納。 次で詳細説明

Slide 29

Slide 29 text

libev c-ares http-parser zlib openssl V8 コアモジュール ユーザモジュール libuv epoll kqueue event port IOCP npm ネイティブ(JS) ビルトイン(C++) この 話題 5. WRAP! WRAP! WRAP!

Slide 30

Slide 30 text

WARP! WRAP! WRAP! V8 JavaScript C++ WRAP var server = require(‘net’).createServer(); new (process.binding('tcp_wrap').TCP)(); 内部 では TCP オブジェクト

Slide 31

Slide 31 text

ビルトインWRAPクラス src/*_wrap.{h,cc} HandleWrap StreamWrap TCPWrap ProcessWrap UDPWrap TimerWrap SignalWrap TTYWrap PipeWrap FSEventWrap ReqWrap FSReqWrap WriteWrap V8 JavaScript C++ WRAP 他にオブジェクト WRAPもあるよ

Slide 32

Slide 32 text

handle と req の違い • handle – I/O が発生してない時でもイベントループを維持 – (例) server.listen() • req – I/Oが発生している時だけイベントループを維持 – (例) http.get()

Slide 33

Slide 33 text

StreamWrapの役割 src/stream_wrap.{h,cc} HandleWrap object_ handle_ close() onclose StreamWrap writeBuffer() ReadStart()/Stop() shutdown() onread TCPWrap connect() onconnection oncomplete WriteWrap SlabAllocator Allocate() Shrink() データの 読み込み データの 書き込み libuv kernel ReqWrap object_ = req_obj data_ = handle (後述) req/handleオブジェクトをlibuuvに 渡して、コールバックで受け取る。

Slide 34

Slide 34 text

var server = require('net').createServer(function(socket) { socket.on('data', function(chunk) { ....; }); }); SlabAllocator src/slab_allocator.{h,cc} Read データ 1MB offset length 16KB単位 libuv+kernel uv_read chunkの正体 読み込み用にあら かじめ1MBのバッ ファスラブを用意。 読み込み毎に Allocate/Shrinkを繰 り返す。 既に読み込 まれた領域

Slide 35

Slide 35 text

libev c-ares http-parser zlib openssl V8 コアモジュール ユーザモジュール libuv epoll kqueue event port IOCP npm ネイティブ(JS) ビルトイン(C++) この辺 の話題 6. Node 起動!

Slide 36

Slide 36 text

Node 起動! src/node_main.cc // UNIX int main(int argc, char *argv[]) { return node::Start(argc, argv); }

Slide 37

Slide 37 text

起動までの大雑把な流れ src/node.cc libuv/V8の 初期化 processオブ ジェクトの生成 src/node.jsの読 み込み イベントループ 開始 uv_run() globalオブジェ クトの生成 • global 関数の登録 • lib/module.js の読み込み • process.nextTick にメインモ ジュールの登録 メインモジュール の実行

Slide 38

Slide 38 text

src/node.js • global変数設定(process/Buffer) • global関数設定(setTimeout等timer関連、console関連) • process関連設定(nextTick、シグナルハンドリング) • process.nextTickでメインモジュール実行 – イベントループを起動した後にメインモジュール の実行をしないと exit や uncaughtException イベ ントを拾えなくなるため。

Slide 39

Slide 39 text

lib/module.js • メインモジュールの読み込み • module/exports/__filename__/__dirname__ の設定 • メインモジュールの実行

Slide 40

Slide 40 text

Nodeクロージャスコープの様子 (function(process) { })(process); ); var global, process; global.setTimeout, global.setInterval, global.console(); process.nextTick( src/node.cc src/node.js lib/module.js Module.runMain ./foo.js console.log(‘hello’); require(‘vm’).runInThisContext( “foo.js” ); global._filename,global_dirname; global.require(), global.module, global.exports;

Slide 41

Slide 41 text

7. Nodeの開発に参加しよう 注意事項を理解してあなたも AUTHOR に、 https://github.com/joyent/node/wiki/Contributing

Slide 42

Slide 42 text

あなたも Node の Authorに、 パッチが採用されると掲載してもらえます。

Slide 43

Slide 43 text

CLA (Contributor License Agreement) http://www.nodejs.org/cla.html • パッチ等のOSSプロジェクトに対する貢献物の 知的財産権に関する同意書 • a non-exclusive, irrevocable, worldwide, royalty-free, sublicenseable, transferable license をNodeJSに譲渡する等々・・・ • 法律用語で難しいので必ず原文を参照のこと。 • 電子サインもしくはスキャンしてメール送付。 • パッチが取り込まれる前に必要。あらかじめ 内容を確認しておきましょう。

Slide 44

Slide 44 text

コーディング規約 • JavaScript – ES5 – Google JavaScript Style Guide に従います。 – コンマファースト、セミコロン無しじゃありません。 – _proto_は使わない。(一部例外有) – make jslint でスタイル確認。 make test すると自動実行。 • C++ – Google C++ Style Guide に従います。 – 2SPインデントKNFスタイル、80文字/行、関数2行空けetc – STL/BOOSTを使わない – Better C っぽい。(個人的な感想) – libuv は c89 です。(thread safeに) – make cpplint でスタイル確認(がエラーが大量!)。

Slide 45

Slide 45 text

コミットメッセージ • 1行目:50文字以内 (修正箇所 : サマリーを記述) • 2行目 空行 • 3行目以降:80文字以内/行 (詳細説明を記述)

Slide 46

Slide 46 text

ユニットテスト • バグが存在し、修正されていることを確認 • 新機能が正しく動作していることを確認 • test/common.js (テスト共通で使う変数) • test/fixture/ (フィクスチャの保管場所) • test/simple/test-xxx-yyy.js (テストファイル) make test で他のテストがデグってないか確認

Slide 47

Slide 47 text

プルリクエストを出す前に • 機能追加/変更 – Node Core vs UserLand https://github.com/joyent/node/wiki/node-core-vs- userland – ユースケースを提示する – APIのStability Indexに注意 – 大きい変更は事前にMLで相談 • バグフィックス – 客観的な再現データを提示 – 本当に make test したか? – できればマルチOSで確認。

Slide 48

Slide 48 text

以上です。 Any Questions?