東京Node学園祭2018の発表資料です https://nodefest.jp/2018/
おまいらちゃんとおまいらちゃんとリソース解放してますかリソース解放してますか(2018/11/23)(2018/11/23)⼩⽥島 太郎 / @shimataro東京NODE学園祭2018 DAY #1東京NODE学園祭2018 DAY #1
View Slide
⾃⼰紹介⾃⼰紹介/ //ウェブリオ株式会社所属(京都)サーバサイド /インフラエンジニア趣味は⼿品⼿品業界→Web業界最近は ⼿打ちうどん にハマりかけてます⼩⽥島 太郎 [email protected][email protected] [email protected]
この発表についてこの発表について対象Node.jsで Webアプリ を作っている⼈他の⾔語からの転⼊⽣特に リクエストごとにプロセスやメモリ空間が独⽴している世界 からの来訪者(e.g., PHP)技術レベル:中級↓スライドはこちら↓https://speakerdeck.com/shimatarohttps://shimataro.github.io/slides/
⽬次⽬次最初にリソースの管理⽅法その1その2その3どうすればいいの?まとめ
それでは始めますそれでは始めます
最初に最初に
リソースとはリソースとはここで⾔う リソースとは、プログラムが確保・解放する必要がある メモリ以外の 資源を指すファイルディスクリプタコネクション特に (R)DBコネクション についてのお話ですメモリはGCが回収してくれるので今回の対象外
リソースの管理⽅法リソースの管理⽅法DBコネクションをどうやって管理してますか?
1.グローバルオブジェクト1.グローバルオブジェクトグローバルなリソースを使いまわすconst mysql = require("mysql");//これを使いまわすconst connection = mysql.createConnection(...);
1.グローバルオブジェクト1.グローバルオブジェクト利点リソースは最⼩限毎回確保する必要がないので⾼速⽋点切断時の再接続処理が必要瞬断MySQLでは8時間アイドル状態だと切断されるトランザクションが他のリクエストを巻き込む開発時は気づきにくい
2.都度確保・都度解放2.都度確保・都度解放必要なときに確保、不要になったら解放const mysql = require("mysql");const pool = mysql.createPool(...);function someFunc() {try {const connection = pool.getConnection(); //必要になったら確保... //何か処理} finally {connection.release(); //使い終わったら解放}}
2.都度確保・都度解放2.都度確保・都度解放利点リクエスト間のトランザクションは独⽴する⽋点1リクエスト中に 無駄に リソースを2つ以上作成する場合あり(関数のネスト等)
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;}
3.リクエスト内で使いまわす3.リクエスト内で使いまわす利点1コネクション/リクエスト 以下⽋点解放忘れに注意リクエスト処理の最後 で解放を忘れると…
3.リクエスト内で使いまわす3.リクエスト内で使いまわすこうなります(転⼊⽣がハマる罠)
3.リクエスト内で使いまわす3.リクエスト内で使いまわす転⼊⽣のハマりどころリクエストを捌き終えても プロセスは⾛り続ける強制解放されないPHPとは違うのだよデストラクタ/ファイナライザがないGCでメモリは回収されるが、 それ以外のリソースは回収されない
どうすればいいの?どうすればいいの?しばらく悩みました。正常終了・異常終了で ⾶ぶイベントが異なるF5連打されると イベントが⾶ばない (ことがある)リソース取得⾃体が⾮同期だとさらにややこしい
こうしとけこうしとけ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();}}
簡単な⽅法簡単な⽅法on-finishedパッケージを使うと楽https://www.npmjs.com/package/on- nishedconst onFinished = require("on-finished");function middleware(req, res, next) { // Express.jsのミドルウェアreq.connection = ...;onFinished(res, () => {req.connection.release(); //ここで解放するだけ!});}
ちなみに…ちなみに…グローバルオブジェクトを使いまわす場合は切断時にしっかり再接続してトランザクション時に新しくリソースを作るうまくやってくれるORMを使おう (e.g., Sequelize)でも、処理完了のタイミングを知っておくのも役に⽴ちます
まとめまとめリソース管理には注意しよう他の⾔語から来た⼈は特に注意処理完了の検知は 3箇所 で!パッケージ を使うと楽だよORMを使うともっと楽だようどん が好きな⼈は声をかけてね!
おまけおまけ
関⻄でもNODE学園!関⻄でもNODE学園!東⻄で盛り上げていきましょう!関⻄Node学園 梅⽥キャンパス 1時限⽬(04/20)関⻄Node学園 梅⽥キャンパス 2時限⽬(07/05)関⻄Node学園 3時限⽬(08/03)関⻄Node学園 4時限⽬(11/02)
ありがとうございましたありがとうございました