MongoDBで node-mongodb-native ドライバーを使用してWebサイトを作成しています。
接続の管理方法についていくつか質問があります。
すべてのリクエストに対して1つのMongoDB接続を使用するだけで十分ですか?パフォーマンスの問題はありますか?そうでない場合、アプリケーション全体で使用するグローバル接続をセットアップできますか?
そうでない場合、リクエストが到着したときに新しい接続を開き、リクエストを処理したときにそれを閉じるといいですか?接続を開いたり閉じたりするのは高価ですか?
グローバル接続プールを使用する必要がありますか?ドライバーにはネイティブ接続プールがあると聞きました。それは良い選択ですか?
接続プールを使用する場合、使用する接続数はいくつですか?
他に気をつけるべきことはありますか?
node-mongodb-nativeのプライマリコミッターは言う :
アプリを起動してdbオブジェクトを再利用するときに、MongoClient.connectを一度開きます。各.connectが新しい接続プールを作成するシングルトン接続プールではありません。
そのため、質問に直接回答するには、MongoClient.connect()の結果であるdbオブジェクトを再利用します。これにより、プーリングが可能になり、各dbアクションで接続を開いたり閉じたりするのに比べて、速度が顕著に向上します。
Node.jsアプリケーションの起動時に新しい接続を開き、既存のdb
接続オブジェクトを再利用します。
/server.js
import express from 'express';
import Promise from 'bluebird';
import logger from 'winston';
import { MongoClient } from 'mongodb';
import config from './config';
import usersRestApi from './api/users';
const app = express();
app.use('/api/users', usersRestApi);
app.get('/', (req, res) => {
res.send('Hello World');
});
// Create a MongoDB connection pool and start the application
// after the database connection is ready
MongoClient.connect(config.database.url, { promiseLibrary: Promise }, (err, db) => {
if (err) {
logger.warn(`Failed to connect to the database. ${err.stack}`);
}
app.locals.db = db;
app.listen(config.port, () => {
logger.info(`Node.js app is listening at http://localhost:${config.port}`);
});
});
/api/users.js
import { Router } from 'express';
import { ObjectID } from 'mongodb';
const router = new Router();
router.get('/:id', async (req, res, next) => {
try {
const db = req.app.locals.db;
const id = new ObjectID(req.params.id);
const user = await db.collection('user').findOne({ _id: id }, {
email: 1,
firstName: 1,
lastName: 1
});
if (user) {
user.id = req.params.id;
res.send(user);
} else {
res.sendStatus(404);
}
} catch (err) {
next(err);
}
});
export default router;
MongoDB接続を管理するコードを次に示します。
var MongoClient = require('mongodb').MongoClient;
var url = require("../config.json")["MongoDBURL"]
var option = {
db:{
numberOfRetries : 5
},
server: {
auto_reconnect: true,
poolSize : 40,
socketOptions: {
connectTimeoutMS: 500
}
},
replSet: {},
mongos: {}
};
function MongoPool(){}
var p_db;
function initPool(cb){
MongoClient.connect(url, option, function(err, db) {
if (err) throw err;
p_db = db;
if(cb && typeof(cb) == 'function')
cb(p_db);
});
return MongoPool;
}
MongoPool.initPool = initPool;
function getInstance(cb){
if(!p_db){
initPool(cb)
}
else{
if(cb && typeof(cb) == 'function')
cb(p_db);
}
}
MongoPool.getInstance = getInstance;
module.exports = MongoPool;
サーバーを起動したら、initPool
を呼び出します
require("mongo-pool").initPool();
次に、他のモジュールで次のことを実行できます。
var MongoPool = require("mongo-pool");
MongoPool.getInstance(function (db){
// Query your MongoDB database.
});
これは MongoDBドキュメント に基づいています。それを見てください。
Express.jsをお持ちの場合、プールなしでリクエスト間でMongoDB接続をキャッシュおよび共有するために express-mongo-db を使用できます(受け入れられた答えは接続を共有する正しい方法であるため) 。
そうでない場合-あなたは、そのソースコードを見て、別のフレームワークでそれを使用することができます。
単一の自己完結型モジュールでmongo接続プールを管理します。このアプローチには2つの利点があります。まず、コードをモジュール化してテストしやすくします。第二に、データベース接続オブジェクトの場所ではないリクエストオブジェクトで、データベース接続を混同することを強制しない。 (JavaScriptの性質を考えると、ライブラリコードによって構築されたオブジェクトに何かを混在させることは非常に危険だと思います)。そのため、2つのメソッドをエクスポートするモジュールのみを検討する必要があります。 connect = () => Promise
およびget = () => dbConnectionObject
。
このようなモジュールを使用すると、まずデータベースに接続できます
// runs in boot.js or what ever file your application starts with
const db = require('./myAwesomeDbModule');
db.connect()
.then(() => console.log('database connected'))
.then(() => bootMyApplication())
.catch((e) => {
console.error(e);
// Always hard exit on a database connection error
process.exit(1);
});
飛行中のアプリは、DB接続が必要なときにget()
を呼び出すだけです。
const db = require('./myAwesomeDbModule');
db.get().find(...)... // I have excluded code here to keep the example simple
以下と同じ方法でdbモジュールを設定すると、データベース接続がない限りアプリケーションが起動しないようにする方法があるだけでなく、エラーになるデータベース接続プールにグローバルにアクセスする方法もあります接続していない場合。
// myAwesomeDbModule.js
let connection = null;
module.exports.connect = () => new Promise((resolve, reject) => {
MongoClient.connect(url, option, function(err, db) {
if (err) { reject(err); return; };
resolve(db);
connection = db;
});
});
module.exports.get = () => {
if(!connection) {
throw new Error('Call connect first!');
}
return connection;
}
私はアプリでredis接続でgeneric-poolを使用しています-私はそれを強くお勧めします。そのジェネリックと私はそれがmysqlで動作することを間違いなく知っているので、あなたはそれとmongoに問題があるとは思わない
http://mongoosejs.com/docs/api.html
Mongooseのソースを確認してください。接続を開いてモデルオブジェクトにバインドし、モデルオブジェクトが必要なときにDBに接続します。ドライバーは接続プーリングを処理します。
接続をサービスとして作成し、必要に応じて再利用する必要があります。
// db.service.js
import { MongoClient } from "mongodb";
import database from "../config/database";
const dbService = {
db: undefined,
connect: callback => {
MongoClient.connect(database.uri, function(err, data) {
if (err) {
MongoClient.close();
callback(err);
}
dbService.db = data;
console.log("Connected to database");
callback(null);
});
}
};
export default dbService;
私のApp.jsサンプル
// App Start
dbService.connect(err => {
if (err) {
console.log("Error: ", err);
process.exit(1);
}
server.listen(config.port, () => {
console.log(`Api runnning at ${config.port}`);
});
});
好きな場所で使用します
import dbService from "db.service.js"
const db = dbService.db
私はプロジェクトに最小限の接続を作成し、利用可能な接続を再利用するように、コードに接続プーリングを実装するためにプロジェクトに以下のコードを実装しました
/* Mongo.js*/
var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://localhost:27017/yourdatabasename";
var assert = require('assert');
var connection=[];
// Create the database connection
establishConnection = function(callback){
MongoClient.connect(url, { poolSize: 10 },function(err, db) {
assert.equal(null, err);
connection = db
if(typeof callback === 'function' && callback())
callback(connection)
}
)
}
function getconnection(){
return connection
}
module.exports = {
establishConnection:establishConnection,
getconnection:getconnection
}
/*app.js*/
// establish one connection with all other routes will use.
var db = require('./routes/mongo')
db.establishConnection();
//you can also call with callback if you wanna create any collection at starting
/*
db.establishConnection(function(conn){
conn.createCollection("collectionName", function(err, res) {
if (err) throw err;
console.log("Collection created!");
});
};
*/
// anyother route.js
var db = require('./mongo')
router.get('/', function(req, res, next) {
var connection = db.getconnection()
res.send("Hello");
});
接続プーリングを実装するための最良のアプローチは、MongoClientによって返された接続オブジェクトでデータベース名を保持する1つのグローバル配列変数を作成し、データベースに接続する必要があるときにその接続を再利用することです。
Server.jsでvar global.dbconnections = [];を定義します
ConnectionService.jsという名前のサービスを作成します。 getConnectionとcreateConnectionの2つのメソッドがあります。したがって、ユーザーがgetConnection()を呼び出すと、グローバル接続変数で詳細が検索され、既に存在する場合は接続の詳細が返されます。それ以外の場合は、createConnection()が呼び出され、接続の詳細が返されます。
Db_nameを使用してこのサービスを呼び出すと、すでに接続されている場合は接続オブジェクトが返され、新しい接続が作成されて返されます。
それが役に立てば幸い :)
ConnectionService.jsコードは次のとおりです。
var mongo = require('mongoskin');
var mongodb = require('mongodb');
var Q = require('q');
var service = {};
service.getConnection = getConnection ;
module.exports = service;
function getConnection(appDB){
var deferred = Q.defer();
var connectionDetails=global.dbconnections.find(item=>item.appDB==appDB)
if(connectionDetails){deferred.resolve(connectionDetails.connection);
}else{createConnection(appDB).then(function(connectionDetails){
deferred.resolve(connectionDetails);})
}
return deferred.promise;
}
function createConnection(appDB){
var deferred = Q.defer();
mongodb.MongoClient.connect(connectionServer + appDB, (err,database)=>
{
if(err) deferred.reject(err.name + ': ' + err.message);
global.dbconnections.Push({appDB: appDB, connection: database});
deferred.resolve(database);
})
return deferred.promise;
}