Skip to content

Instantly share code, notes, and snippets.

@shimataro
Last active January 8, 2018 11:31
Show Gist options
  • Save shimataro/10c378802f078990581873d640f5f3f4 to your computer and use it in GitHub Desktop.
Save shimataro/10c378802f078990581873d640f5f3f4 to your computer and use it in GitHub Desktop.
リソースマネージャーを作ってみた
export class ResourceManager {
constructor() {
this._resourceFunctionsMap = {};
this._resourceSingletonMap = {};
this._closeCallbacks = [];
this._closed = false;
}
/**
* リソース登録
* @param {string} name
* @param {function(*)} open
* @param {function(*)} close
*/
register(name, open, close) {
this._resourceFunctionsMap[name] = {
open: open,
close: close,
};
}
/**
* リソース取得
* @param {string} name
* @param {?*} options
* @return {*}
*/
open(name, options = null) {
const resourceFunctionsMap = this._resourceFunctionsMap;
if (!resourceFunctionsMap.hasOwnProperty(name)) {
// リソース名が登録されていない
throw new Error(`ResourceManager: resource name "${name}" is unregistered`);
}
if (this._closed) {
// すでに閉じられていたらエラー
// (クライアントからの接続が途中で切れた場合、リソース確保前にcloseメソッドがコールされる場合がある)
throw new Error(`ResourceManager: resources are already closed`);
}
const resourceFunctions = resourceFunctionsMap[name];
const resource = resourceFunctions.open(options);
this._closeCallbacks.push(() => {
resourceFunctions.close(resource);
});
return resource;
}
/**
* リソース取得(Singleton)
* @param {string} name
* @param {?*} options
* @return {*}
*/
openSingle(name, options = null) {
const key = JSON.stringify([name, options]);
const resourceSingletonMap = this._resourceSingletonMap;
if (!resourceSingletonMap.hasOwnProperty(key)) {
resourceSingletonMap[key] = this.open(name, options);
}
return resourceSingletonMap[key];
}
/**
* 取得したリソースをすべて解放
* res.on("finish") など、処理完了のタイミングでコール
*/
close() {
const callbacks = this._closeCallbacks;
while (callbacks.length > 0) {
// 登録と逆順にコール
const callback = callbacks.pop();
callback();
}
this._closed = true;
}
}
import express from "express";
// メイン処理
{
const app = express();
app.listen(3000);
app
.use(middleware)
.get("/", (req, res, next) => {
// openSingleは同じリソース名・同じオプションで何度コールしても最初のリソースを使いまわす
const conn = req.objResourceManager.openSingle("mysql", {/* 接続オプション */});
// いろいろ処理
res.send("OK");
});
}
/**
* Express.jsのミドルウェア(最初に登録)
*/
function middleware(req, res, next) {
// リソースマネージャーをreqのプロパティに追加
const objResourceManager = new ResourceManager();
req.objResourceManager = objResourceManager;
// リソース名・取得方法・解放方法を指定
objResourceManager.register(
"mysql",
(options) => {
return mysql.createConnection(options);
},
(conn) => {
conn.end();
});
// 終了時にリソース解放
res
.on("close", () => {
// 切断
objResourceManager.close();
})
.on("finish", () => {
// 処理完了
objResourceManager.close();
});
if (res.socket.destroyed) {
// そもそもこの時点で切断されていた
objResourceManager.close();
}
next();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment