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

D79a0829d09a325462fc9a1462ec503b?s=47 shimataro
November 23, 2018

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

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

D79a0829d09a325462fc9a1462ec503b?s=128

shimataro

November 23, 2018
Tweet

Transcript

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

    東京NODE 学園祭2018 DAY #1 東京NODE 学園祭2018 DAY #1
  2. ⾃⼰紹介 ⾃⼰紹介 / / / ウェブリオ株式会社所属(京都) サーバサイド / インフラエンジニア 趣味は⼿品

    ⼿品業界→Web 業界 最近は ⼿打ちうどん にハマりかけてます ⼩⽥島 太郎 shimataro@GitHub odashima.taro@Facebook shimataro999@Twitter
  3. この発表について この発表について 対象 Node.js で Web アプリ を作っている⼈ 他の⾔語からの転⼊⽣ 特に

    リクエストごとにプロセスやメモリ空間が 独⽴している世界 からの来訪者(e.g., PHP ) 技術レベル: 中級 ↓スライドはこちら↓ https://speakerdeck.com/shimataro https://shimataro.github.io/slides/
  4. ⽬次 ⽬次 最初に リソースの管理⽅法 その1 その2 その3 どうすればいいの? まとめ

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

  6. 最初に 最初に

  7. リソースとは リソースとは ここで⾔う リソースとは、プログラムが確保・解放 する必要がある メモリ以外の 資源を指す ファイルディスクリプタ コネクション 特に

    (R)DB コネクション についてのお話です メモリはGC が回収してくれるので今回の対象外
  8. リソースの管理⽅法 リソースの管理⽅法 DB コネクションをどうやって管理してますか?

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

    これを使いまわす const connection = mysql.createConnection(...);
  10. 1. グローバルオブジェクト 1. グローバルオブジェクト 利点 リソースは最⼩限 毎回確保する必要がないので⾼速 ⽋点 切断時の再接続処理が必要 瞬断

    MySQL では8 時間アイドル状態だと切断される トランザクションが他のリクエストを巻き込む 開発時は気づきにくい
  11. 2. 都度確保・都度解放 2. 都度確保・都度解放 必要なときに確保、不要になったら解放 const mysql = require("mysql"); const

    pool = mysql.createPool(...); function someFunc() { try { const connection = pool.getConnection(); // 必要になったら確保 ... // 何か処理 } finally { connection.release(); // 使い終わったら解放 } }
  12. 2. 都度確保・都度解放 2. 都度確保・都度解放 利点 リクエスト間のトランザクションは独⽴する ⽋点 1 リクエスト中に 無駄に

    リソースを2 つ以上作成す る場合あり(関数のネスト等)
  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; }
  14. 3. リクエスト内で使いまわす 3. リクエスト内で使いまわす 利点 1 コネクション/ リクエスト 以下 ⽋点

    解放忘れに注意 リクエスト処理の最後 で解放を忘れると…
  15. 3. リクエスト内で使いまわす 3. リクエスト内で使いまわす こうなります (転⼊⽣がハマる罠)

  16. 3. リクエスト内で使いまわす 3. リクエスト内で使いまわす 転⼊⽣のハマりどころ リクエストを捌き終えても プロセスは⾛り続ける 強制解放されない PHP とは違うのだよ

    デストラクタ/ ファイナライザがない GC でメモリは回収されるが、 それ以外のリソー スは回収されない
  17. どうすればいいの? どうすればいいの? しばらく悩みました。 正常終了・異常終了で ⾶ぶイベントが異なる F5 連打されると イベントが⾶ばない (ことがあ る)

    リソース取得⾃体が⾮同期だとさらにややこしい
  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(); } }
  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(); // ここで解放するだけ! }); }
  20. ちなみに… ちなみに… グローバルオブジェクトを使いまわす場合は 切断時にしっかり再接続して トランザクション時に新しくリソースを作る うまくやってくれるORM を使おう (e.g., Sequelize )

    でも、処理完了のタイミングを知っておくのも役に⽴ ちます
  21. まとめ まとめ リソース管理には注意しよう 他の⾔語から来た⼈は特に注意 処理完了の検知は 3 箇所 で! パッケージ を使うと楽だよ

    ORM を使うともっと楽だよ うどん が好きな⼈は声をかけてね!
  22. おまけ おまけ

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

    ) 関⻄Node 学園 梅⽥キャンパス 2 時限⽬(07/05 ) 関⻄Node 学園 3 時限⽬(08/03 ) 関⻄Node 学園 4 時限⽬(11/02 )
  24. ありがとうございました ありがとうございました