Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Nodeコアの歩き方 ~Nodeの開発に参加しよう~ [Node.js]

Nodeコアの歩き方 ~Nodeの開発に参加しよう~ [Node.js]

東京Node学園祭2012

Shigeki Ohtsu

November 17, 2012
Tweet

More Decks by Shigeki Ohtsu

Other Decks in Programming

Transcript

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  5. 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 しよう!

    View Slide

  6. 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で生成)

    View Slide

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

    View Slide

  8. • C++ で記述
    • node_extensions.h 経由で
    参照
    • libuv やV8等の他のライブ
    ラリを利用するために使わ
    れる。
    • process.binding()で読み込

    コアモジュールの種類
    ビルトインモジュール
    src/*.cc
    • JavaScriptで記述
    • node_natives.h 中に格納
    (後述)
    • 主にユーザモジュールに提
    供するAPIとして使われる。
    • module.require()で読み込

    ネイティブモジュール
    src/node.js, lib/*.js

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  13. 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

    View Slide

  14. 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() を使おう!
    去年学園祭でもやったフィボナッチ計算

    View Slide

  15. じゃ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

    View Slide

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

    View Slide

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

    View Slide

  18. ネイティブモジュールの構造概要
    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の
    土地勘を持つ

    View Slide

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

    View Slide

  20. 全部同じに書けます
    (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の
    土地勘を持つ

    View Slide

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

    View Slide

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

    View Slide

  23. この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の
    土地勘を持つ

    View Slide

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

    View Slide

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

    View Slide

  26. 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

    View Slide

  27. 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()

    View Slide

  28. 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オブジェクトはクラスの プライベー
    トメンバーに格納。
    次で詳細説明

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  33. 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に
    渡して、コールバックで受け取る。

    View Slide

  34. 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を繰
    り返す。
    既に読み込
    まれた領域

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  40. 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;

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  44. コーディング規約
    • 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 でスタイル確認(がエラーが大量!)。

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  48. 以上です。
    Any Questions?

    View Slide