2013-03-28 6 views
21

10gen이 기본 node.js 드라이브를 사용하여 mongodb (2.2.2)와 함께 node.js를 시도합니다.Mongodb 연결을 처리하는 올바른 방법은 무엇입니까?

처음에는 모든 것이 잘되었습니다. 그러나 동시성 벤치마킹 부분에 오면 많은 오류가 발생합니다. 자주 연결/닫기 1000 concurrencies에 발생할 수 MongoDB의 오류 등으로 더 이상의 요청을 거부 :

Error: failed to connect to [localhost:27017] 

Error: Could not locate any valid servers in initial seed list 

Error: no primary server found in set 

또한, 명시 적으로 가까운없이 클라이언트 종료를 많이, 그것은 감지하고 그들을 닫습니다 MongoDB를 분 정도 걸릴 수 있습니다 경우. 비슷한 연결 문제가 발생할 수도 있습니다. (/var/log/mongodb/mongodb.log를 사용하여 연결 상태를 확인하십시오.)

나는 많은 시도를했습니다. 매뉴얼에 따르면 mongodb에는 연결 제한이 없지만 poolSize 옵션은 나에게 아무런 영향을 미치지 않는 것으로 보입니다.

node-mongodb-native 모듈에서만 작업 했으므로 결국 어떤 문제가 발생했는지 잘 모르겠습니다. 다른 언어 및 드라이버의 성능은 어떻습니까?

추신 : 현재, 자체 유지 관리 된 풀만 사용하면 알아 낸 유일한 해결책이지만 사용하면 복제본 세트로 문제를 해결할 수 없습니다. 내 테스트에 따르면 복제본 세트는 독립형 mongodb보다 훨씬 적은 연결로 보인다. 그러나 이것이 왜 발생하는지 전혀 모릅니다.

동시성 테스트 코드 :

var MongoClient = require('mongodb').MongoClient; 

var uri = "mongodb://192.168.0.123:27017,192.168.0.124:27017/test"; 

for (var i = 0; i < 1000; i++) { 
    MongoClient.connect(uri, { 
     server: { 
      socketOptions: { 
       connectTimeoutMS: 3000 
      } 
     }, 
    }, function (err, db) { 
     if (err) { 
      console.log('error: ', err); 
     } else { 
      var col = db.collection('test'); 
      col.insert({abc:1}, function (err, result) { 
       if (err) { 
        console.log('insert error: ', err); 
       } else { 
        console.log('success: ', result); 
       } 
       db.close() 
      }) 
     } 
    }) 
} 

일반 풀 솔루션 :

Node.js를 이후
var MongoClient = require('mongodb').MongoClient; 
var poolModule = require('generic-pool'); 

var uri = "mongodb://localhost/test"; 

var read_pool = poolModule.Pool({ 
    name  : 'redis_offer_payment_reader', 
    create : function(callback) { 
     MongoClient.connect(uri, {}, function (err, db) { 
      if (err) { 
       callback(err); 
      } else { 
       callback(null, db); 
      } 
     }); 
    }, 
    destroy : function(client) { client.close(); }, 
    max  : 400, 
    // optional. if you set this, make sure to drain() (see step 3) 
    min  : 200, 
    // specifies how long a resource can stay idle in pool before being removed 
    idleTimeoutMillis : 30000, 
    // if true, logs via console.log - can also be a function 
    log : false 
}); 


var size = []; 
for (var i = 0; i < 100000; i++) { 
    size.push(i); 
} 

size.forEach(function() { 
    read_pool.acquire(function (err, db) { 
     if (err) { 
      console.log('error: ', err); 
     } else { 
      var col = db.collection('test'); 
      col.insert({abc:1}, function (err, result) { 
       if (err) { 
        console.log('insert error: ', err); 
       } else { 
        //console.log('success: ', result); 
       } 
       read_pool.release(db); 
      }) 
     } 
    }) 
}) 

답변

22

는 열고 당신처럼 (각 요청에 대한 연결을 종료해서는 안 단일 스레드입니다 다른 다중 스레드 환경에서 할 수 있습니다.)

MongoDB node.js 클라이언트 모듈을 작성한 사람의 견적입니다 :

"응용 프로그램을 부팅하고 db 개체를 다시 사용할 때 MongoClient.connect를 한 번 열어보십시오. 그것은 각각 singleton connection pool이 아닙니다. connect 은 새로운 연결 풀을 생성합니다. 그래서 모든 요청에 걸쳐 다시 그것을 한 번 [D]를 엽니 다. "- christkv https://groups.google.com/forum/#!msg/node-mongodb-native/mSGnnuG8C1o/Hiaqvdu1bWoJ

+0

Node.js를가 단일 프로세스 및 이벤트 모델을 사용하지만 IO 작업은 항상 비동기해야하지만. 나는 내 이슈의 원인이 아닐지도 모른다. 하지만, 내가 인용 한대로 해보겠습니다. 고마워요. – Jack

+2

첫 번째 예에서는 요점을 놓치고 있습니다. 이 코드는 Mongo 앱을 1000 번 시작하는 것과 같습니다. 앱에서 한 번만 .connect를 호출해야합니다. –

+0

네, 그게 문제입니다. 그 이유는 MongoClient가 다른 dbs보다 비용이 많이 드는 연결 풀을 보유하고 있기 때문입니다. 하지만 node.js 단일 프로세스 모델의 문제는 아닙니다. – Jack

3

을 헥터의 조언에보고 한 후. 나는 Mongodb의 연결이 내가 사용했던 다른 데이터베이스와 상당히 다른 것을 발견했다. MongoClient 그래서

server:{poolSize: n} 

에 의해 정의 된 풀 사이즈 각 MongoClient 열을 위해 자신의 연결 풀, poolSize 오픈 5 MongoClient 연결이 있습니다 : 가장 큰 차이점은 nodejs에서 기본 드라이브, 100을 의미 총 5 * 100 = 대상 Mongodb Uri에 500 연결. 이 경우 MongoClient 연결을 자주 닫는 &을 열면 호스트에 막대한 부담이되며 결국 연결 문제가 발생할 수 있습니다. 그래서 내가 처음부터 많은 어려움을 겪었습니다.

내 코드가 그런 방식으로 작성되었으므로 연결 풀을 사용하여 각각의 URI가있는 단일 연결을 저장하고 poolSize와 동일한 크기의 간단한 병렬 리미터를 사용하여로드 최대 연결 오류를 방지합니다. .

여기 내 코드입니다 :

/*npm modules start*/ 
var MongoClient = require('mongodb').MongoClient; 
/*npm modules end*/ 

// simple resouce limitation module, control parallel size 
var simple_limit = require('simple_limit').simple_limit; 

// one uri, one connection 
var client_pool = {}; 

var default_options = { 
    server: { 
     auto_reconnect:true, poolSize: 200, 
     socketOptions: { 
      connectTimeoutMS: 1000 
     } 
    } 
} 

var mongodb_pool = function (uri, options) { 
    this.uri = uri; 
    options = options || default_options; 
    this.options = options; 
    this.poolSize = 10; // default poolSize 10, this will be used in generic pool as max 

    if (undefined !== options.server && undefined !== options.server.poolSize) { 
     this.poolSize = options.server.poolSize;// if (in)options defined poolSize, use it 
    } 
} 

// cb(err, db) 
mongodb_pool.prototype.open = function (cb) { 
    var self = this; 
    if (undefined === client_pool[this.uri]) { 
     console.log('new'); 

     // init pool node with lock and wait list with current callback 
     client_pool[this.uri] = { 
      lock: true, 
      wait: [cb] 
     } 

     // open mongodb first 
     MongoClient.connect(this.uri, this.options, function (err, db) { 
      if (err) { 
       cb(err); 
      } else { 
       client_pool[self.uri].limiter = new simple_limit(self.poolSize); 
       client_pool[self.uri].db = db; 

       client_pool[self.uri].wait.forEach(function (callback) { 
        client_pool[self.uri].limiter.acquire(function() { 
         callback(null, client_pool[self.uri].db) 
        }); 
       }) 

       client_pool[self.uri].lock = false; 
      } 
     }) 
    } else if (true === client_pool[this.uri].lock) { 
     // while one is connecting to the target uri, just wait 
     client_pool[this.uri].wait.push(cb); 
    } else { 
     client_pool[this.uri].limiter.acquire(function() { 
      cb(null, client_pool[self.uri].db) 
     }); 
    } 
} 

// use close to release one connection 
mongodb_pool.prototype.close = function() { 
    client_pool[this.uri].limiter.release(); 
} 

exports.mongodb_pool = mongodb_pool; 
+0

안녕하세요,이 문제가 발생했습니다. 'var simple_limit = require ('simple_limit')를 지나칠 수 있었습니까? simple_limit; 'simple_limit.js 코드? 'mongodb_pool'을 어떻게 사용합니까? 고마워 ~ – Eddy

관련 문제