$30 off During Our Annual Pro Sale. View Details »

おまいらちゃんとリソース解放してますか / Remember to close resources!

shimataro
November 23, 2018

おまいらちゃんとリソース解放してますか / Remember to close resources!

東京Node学園祭2018の発表資料です
https://nodefest.jp/2018/

shimataro

November 23, 2018
Tweet

More Decks by shimataro

Other Decks in Technology

Transcript

  1. おまいらちゃんと
    おまいらちゃんと
    リソース解放してますか
    リソース解放してますか
    (2018/11/23)
    (2018/11/23)
    ⼩⽥島 太郎 / @shimataro
    東京NODE
    学園祭2018 DAY #1
    東京NODE
    学園祭2018 DAY #1

    View Slide

  2. ⾃⼰紹介
    ⾃⼰紹介
    / /
    /
    ウェブリオ株式会社所属(京都)
    サーバサイド /
    インフラエンジニア
    趣味は⼿品
    ⼿品業界→Web
    業界
    最近は ⼿打ちうどん にハマりかけてます
    ⼩⽥島 太郎 shimataro@GitHub
    odashima.taro@Facebook shimataro999@Twitter

    View Slide

  3. この発表について
    この発表について
    対象
    Node.js
    で Web
    アプリ を作っている⼈
    他の⾔語からの転⼊⽣
    特に リクエストごとにプロセスやメモリ空間が
    独⽴している世界 からの来訪者(e.g., PHP

    技術レベル:
    中級
    ↓スライドはこちら↓
    https://speakerdeck.com/shimataro
    https://shimataro.github.io/slides/

    View Slide

  4. ⽬次
    ⽬次
    最初に
    リソースの管理⽅法
    その1
    その2
    その3
    どうすればいいの?
    まとめ

    View Slide

  5. それでは始めます
    それでは始めます

    View Slide

  6. 最初に
    最初に

    View Slide

  7. リソースとは
    リソースとは
    ここで⾔う リソースとは、プログラムが確保・解放
    する必要がある メモリ以外の 資源を指す
    ファイルディスクリプタ
    コネクション
    特に (R)DB
    コネクション についてのお話です
    メモリはGC
    が回収してくれるので今回の対象外

    View Slide

  8. リソースの管理⽅法
    リソースの管理⽅法
    DB
    コネクションをどうやって管理してますか?

    View Slide

  9. 1.
    グローバルオブジェクト
    1.
    グローバルオブジェクト
    グローバルなリソースを使いまわす
    const mysql = require("mysql");
    //
    これを使いまわす
    const connection = mysql.createConnection(...);

    View Slide

  10. 1.
    グローバルオブジェクト
    1.
    グローバルオブジェクト
    利点
    リソースは最⼩限
    毎回確保する必要がないので⾼速
    ⽋点
    切断時の再接続処理が必要
    瞬断
    MySQL
    では8
    時間アイドル状態だと切断される
    トランザクションが他のリクエストを巻き込む
    開発時は気づきにくい

    View Slide

  11. 2.
    都度確保・都度解放
    2.
    都度確保・都度解放
    必要なときに確保、不要になったら解放
    const mysql = require("mysql");
    const pool = mysql.createPool(...);
    function someFunc() {
    try {
    const connection = pool.getConnection(); //
    必要になったら確保
    ... //
    何か処理
    } finally {
    connection.release(); //
    使い終わったら解放
    }
    }

    View Slide

  12. 2.
    都度確保・都度解放
    2.
    都度確保・都度解放
    利点
    リクエスト間のトランザクションは独⽴する
    ⽋点
    1
    リクエスト中に 無駄に リソースを2
    つ以上作成す
    る場合あり(関数のネスト等)

    View Slide

  13. 3.
    リクエスト内で使いまわす
    3.
    リクエスト内で使いまわす
    リクエストオブジェクトにリソースを関連付け、この
    リソースを使いまわす
    (もしくはContinuation Local Storage
    を使⽤)
    const mysql = require("mysql");
    const pool = mysql.createPool(...);
    function getConnection(req) {
    if(req.connection === undefined) {
    // req
    にリソースを関連付ける
    req.connection = pool.getConnection();
    }
    return req.connection;
    }

    View Slide

  14. 3.
    リクエスト内で使いまわす
    3.
    リクエスト内で使いまわす
    利点
    1
    コネクション/
    リクエスト 以下
    ⽋点
    解放忘れに注意
    リクエスト処理の最後 で解放を忘れると…

    View Slide

  15. 3.
    リクエスト内で使いまわす
    3.
    リクエスト内で使いまわす
    こうなります
    (転⼊⽣がハマる罠)

    View Slide

  16. 3.
    リクエスト内で使いまわす
    3.
    リクエスト内で使いまわす
    転⼊⽣のハマりどころ
    リクエストを捌き終えても プロセスは⾛り続ける
    強制解放されない
    PHP
    とは違うのだよ
    デストラクタ/
    ファイナライザがない
    GC
    でメモリは回収されるが、 それ以外のリソー
    スは回収されない

    View Slide

  17. どうすればいいの?
    どうすればいいの?
    しばらく悩みました。
    正常終了・異常終了で ⾶ぶイベントが異なる
    F5
    連打されると イベントが⾶ばない (ことがあ
    る)
    リソース取得⾃体が⾮同期だとさらにややこしい

    View Slide

  18. こうしとけ
    こうしとけ
    3
    箇所で解放
    特に最後は⼤事(F5
    連打対策)
    function middleware(req, res, next) { // Express.js
    のミドルウェア
    req.connection = ...;
    res
    .on("finish", () => {
    req.connection.release(); //
    正常完了時
    })
    .on("close", () => {
    req.connection.release(); //
    異常終了時(通信切断)
    });
    if (res.socket.destroyed) {
    //
    すでに通信が切断されていた(イベントが発⽣しない)
    req.connection.release();
    }
    }

    View Slide

  19. 簡単な⽅法
    簡単な⽅法
    on-finished
    パッケージを使うと楽
    https://www.npmjs.com/package/on- nished
    const onFinished = require("on-finished");
    function middleware(req, res, next) { // Express.js
    のミドルウェア
    req.connection = ...;
    onFinished(res, () => {
    req.connection.release(); //
    ここで解放するだけ!
    });
    }

    View Slide

  20. ちなみに…
    ちなみに…
    グローバルオブジェクトを使いまわす場合は
    切断時にしっかり再接続して
    トランザクション時に新しくリソースを作る
    うまくやってくれるORM
    を使おう (e.g., Sequelize

    でも、処理完了のタイミングを知っておくのも役に⽴
    ちます

    View Slide

  21. まとめ
    まとめ
    リソース管理には注意しよう
    他の⾔語から来た⼈は特に注意
    処理完了の検知は 3
    箇所 で!
    パッケージ を使うと楽だよ
    ORM
    を使うともっと楽だよ
    うどん が好きな⼈は声をかけてね!

    View Slide

  22. おまけ
    おまけ

    View Slide

  23. 関⻄でもNODE
    学園!
    関⻄でもNODE
    学園!
    東⻄で盛り上げていきましょう!
    関⻄Node
    学園 梅⽥キャンパス 1
    時限⽬(04/20

    関⻄Node
    学園 梅⽥キャンパス 2
    時限⽬(07/05

    関⻄Node
    学園 3
    時限⽬(08/03

    関⻄Node
    学園 4
    時限⽬(11/02

    View Slide

  24. ありがとうございました
    ありがとうございました

    View Slide