原文地址:http://cnodejs.org/topic/5190d61263e9f8a542acd83b

mongo数据库在nodejs平台有2个常用驱动,mongodb和mongoose,mongodb接口非常接近mongo数据库原生的操作方式,是helloworld之类演示代码的首选mongo数据库连接驱动,因此成为大部分nodejs初学者最先接触的mongo数据库驱动。初学者在学会mongo连接的同时,却也可悲的被helloword这种演示性质的数据库操作习惯潜移默化了。
cat test.js

var server_options={};var db_options={
w:-1,// 设置w=-1是mongodb 1.2后的强制要求,见官方api文档
logger:{
doDebug:true,
debug:function(msg,obj){
console.log('[debug]',msg);},
log:function(msg,obj){
console.log('[log]',msg);},
error:function(msg,obj){
console.log('[error]',msg);}}};var mongodb =require("mongodb"),
mongoserver =new mongodb.Server('localhost',27017,server_options ),
db =new mongodb.Db('test', mongoserver, db_options);function test_save(){//前一个db和后一个db,是同一个对象。
db.open(function(err,db){if(err)return console.error(err);
console.log('* mongodb connected');
db.collection('foo').save({test:1},function(err,result){
console.log(result);
db.close();});})}
test_save();

这是个随处可见,大家非常熟悉的mongo数据库helloword代码:设置连接参数,open, 访问collection, close。 唯一不同的是为了显示代码背后的实际运作,我给db_options加上一个日志(logger)选项。
node test.js, 客户端输出信息:

[debug] opened connection
[debug] opened connection
[debug] opened connection
[debug] opened connection
[debug] opened connection
[debug] writing command to mongodb
* mongodb connected
[debug] writing command to mongodb
{ test:1, _id:51908a93718f6ad00c000001}[debug] closed connection
[debug] closed connection
[debug] closed connection
[debug] closed connection
[debug] closed connection

服务端mongo数据库的输出日志:

MonMay1312:54:33[initandlisten] connection accepted from127.0.0.1:2815#51MonMay1312:54:33[initandlisten] connection accepted from127.0.0.1:2816#52MonMay1312:54:33[initandlisten] connection accepted from127.0.0.1:2817#53MonMay1312:54:33[initandlisten] connection accepted from127.0.0.1:2818#54MonMay1312:54:33[initandlisten] connection accepted from127.0.0.1:2819#55MonMay1312:54:33[conn51]end connection 127.0.0.1:2815MonMay1312:54:33[conn52]end connection 127.0.0.1:2816MonMay1312:54:33[conn53]end connection 127.0.0.1:2817MonMay1312:54:33[conn54]end connection 127.0.0.1:2818MonMay1312:54:33[conn55]end connection 127.0.0.1:2819

客户端和服务器端的日志都表明,db.open了5个连接,而并非一般同学所想象的1个连接。why?这是因为mongoserver = new mongodb.Server('localhost', 27017,server_options )的serveroptions有个poolSize选项,默认值是5(见官方api文档)。db对象不仅扮演着与mongo数据库通讯的中间人角色,还同时是一个连接池。默认设置的情况下,helloword代码打开一个有着5个连接的连接池,然后再关闭这个连接池。作为ran and quit的演示,这个操作流程当然没问题,但如果放到http server的应用场景就成大问题了。每次http请求都打开5个数据库连接而后又关闭5个数据库连接,对性能影响可想而知,更为糟糕是open and close的操作流程会导致一个潜在的并发错误。
_cat server_1.js

var server_options={};var db_options={w:-1};var mongodb =require("mongodb"),
mongoserver =new mongodb.Server('localhost',27017,server_options ),
db =new mongodb.Db('test', mongoserver, db_options);var http=require('http');var server=http.createServer(function(req,res){
db.open(function(err,db){if(err)return console.error(err);
console.log('* mongodb connected');
db.collection('foo').save({test:1},function(err,result){
res.end(JSON.stringify(result,null,2));
db.close();});})});
server.listen(8080,function(){
console.log('server listen to %d',this.address().port);});
setTimeout(function(){//http.get('http://localhost:8080',function(res){console.log('request ok')});//http.get('http://localhost:8080',function(res){console.log('request ok')});},2000);

node server.js, 浏览器访问ok,但如果做ab类并发测试或者把倒数2、3行的注释去掉,问题就来了。

c:\nodejs\node_modules\mongodb\lib\mongodb\db.js:224thrownewError("db object already connecting, open cannot be called multi
^
Error: db object already connecting, open cannot be called multiple times

想象db对象是一扇门,open操作相当于开门,开门后才能阅读房间里面的书籍(数据)。当请求1开门后,紧随而来的请求2也想开门但是开不了,因为请求1还没关门(db.close),门还处于“打开”的状态。其实呢,请求2完全没必要再开门,直接尾随请求1进门即可。错误的根源在于我们要打开一扇已经打开的门。how to fix? easy, 从open and close 行为改为 open once and reuse anywhere。程序启动的时候db.open一次,每次http请求直接访问数据库,扔掉db.open/db.close这2个多余的操作。

cat server_2.js

var server_options={'auto_reconnect':true,poolSize:5};var db_options={w:-1};var mongodb =require("mongodb"),
mongoserver =new mongodb.Server('localhost',27017,server_options ),
db =new mongodb.Db('test', mongoserver, db_options); db.open(function(err,db){if(err)throw err;
console.info('mongodb connected');});var http=require('http');var server=http.createServer(function(req,res){
db.collection('foo').save({test:1},function(err,result){
res.end(JSON.stringify(result,null,2));});});
server.listen(8080,function(){
console.log('server listen to %d',this.address().port);});
setTimeout(function(){
http.get('http://localhost:8080',function(res){console.log('request ok')});
http.get('http://localhost:8080',function(res){console.log('request ok')});},2000);

这样改后,虽然没报错了,却引入另一个潜在的问题:当并发访问>5时,因为同时可用的底层数据库连接只有5,从而导致了阻塞。

===================================我是分隔线=====================================

实际应用场景中,直接引用db对象并不是一个好主意。默认情况下,db的poolSize=5,意味着并发只有5, 要提高并发的话,把poolSize拉到10? 20? 50? 100? NO,我们需要的是能动态调整连接数的连接池,既能满足高峰期的连接数要求,也能在空闲期释放闲置的连接,而不是象mongodb的内置连接池那样保持固定连接数。怎么办?重新发明轮子吗?不,重用已有的连接池模块genericpool。
_cat server_3.js

var http=require('http'),
mongodb =require("mongodb"),
poolModule =require('generic-pool');var pool = poolModule.Pool({
name :'mongodb',
create :function(callback){var server_options={'auto_reconnect':false,poolSize:1};var db_options={w:-1};var mongoserver =new mongodb.Server('localhost',27017,server_options );var db=new mongodb.Db('test', mongoserver, db_options);
db.open(function(err,db){if(err)return callback(err);
callback(null,db);});},
destroy :function(db){ db.close();},
max :10,//根据应用的可能最高并发数设置
idleTimeoutMillis :30000,
log :false});var server=http.createServer(function(req,res){
pool.acquire(function(err, db){if(err){
res.statusCode=500;
res.end(JSON.stringify(err,null,2));}else{
db.collection('foo').save({test:1},function(err,result){
res.end(JSON.stringify(result,null,2));
pool.release(db);});}});});
server.listen(8080,function(){
console.log('server listen to %d',this.address().port);});
setTimeout(function(){
http.get('http://localhost:8080',function(res){console.log('request ok')});
http.get('http://localhost:8080',function(res){console.log('request ok')});},2000);

将poolSize设为1,一个db对象只负责一个底层的数据库连接,generic_pool通过控制db对象的数目,间接控制实际的数据库连接数目。如果poolSize还采取默认值5,1db=5连接,由于每次http请求期间我们实际使用到的只是1个连接,其他4个连接根本用不上,处于闲置状态,其结果是浪费资源,拖慢响应速度。

以上。

备注1:本文mongo数据库设置均采用本机安装的默认设置。
备注2:当需建模的业务对象较多时,使用mongoose驱动是个好主意,它自带的连接池比mongodb实用性强。
备注3:mongodb自1.2以后,官方推荐的连接方式改为MongoClient,将参数设置简化为一个URL(详细见http://mongodb.github.io/node-mongodb-native/driver-articles/mongoclient.html)。目前monodb的版本已经1.3,大家也该是时候转变了吧。
以上文generic_pool连接池的初始化为例,看看新旧连接的对比:
旧连接方法

var pool = poolModule.Pool({
name :'mongodb',
create :function(callback){var server_options={'auto_reconnect':false,poolSize:1};var db_options={w:-1};var mongoserver =new mongodb.Server('localhost',27017,server_options );var db=new mongodb.Db('test', mongoserver, db_options);
db.open(function(err,db){if(err)return callback(err);
callback(null,db);});},//......more code here});

新连接方法

var pool = poolModule.Pool({
name :'mongodb',
create :function(callback){
mongodb.MongoClient.connect('mongodb://localhost/test',{
server:{poolSize:1}},function(err,db){
callback(err,db);});},//more code here});

技术讨论]mongodb驱动的正确使用方法的更多相关文章

  1. MongoDB 驱动以及分布式集群读取优先级设置

    本文主要介绍使用MongoDB C驱动读取分布式MongoDB集群时遇到的坑,主要在读取优先级和匹配tag上:同时简单介绍Python驱动.Node.js驱动.Mongoose驱动如何使用读取优先级和 ...

  2. sae-v2ex 一个运行在SAE上的类似v2ex的轻型python论坛 - 技术讨论 - 云计算开发者社区 - Powered by Discuz!

    sae-v2ex 一个运行在SAE上的类似v2ex的轻型python论坛 - 技术讨论 - 云计算开发者社区 - Powered by Discuz! sae-v2ex 一个运行在SAE上的类似v2e ...

  3. CSS开发框架技术OOCSS编写和管理CSS的方法

    目前最流行的CSS开发框架技术当属OOCSS,尽管还有其他类似技术(如BEM).这些方法试图对CSS采用面向对象的编程原则.样式语言与面向对象的设计原则在概念之间存在一定的问题.欠缺经验的人员可能不会 ...

  4. 安装golang的mongodb驱动mgo速记

    这里介绍的方法只适用于Centos平台,测试版本为centos 6.5 下载源码安装实在麻烦,这里采用比较简单的方法给GO安装mongodb驱动 安装mgo之前,需要先安装bzr yum -y ins ...

  5. MongoDB save()方法和insert()方法的区别

    MongoDB save()方法和insert()方法的区别 首先看官方文档怎么说的 Updates an existing document or inserts a new document, d ...

  6. Ubuntu 解决wifi无法打开的问题 安装NVIDIA显卡驱动的正确姿势

    游戏本型号Y7000 win10 Ubuntu16.04双系统 解决wifi无法打开的问题 解决方法: 1.打开终端输入:rfkill list all 出现如下提示::       可以看到,优先级 ...

  7. MongoDB全球云端技术盛会MongoDB.live

    MongoDB全球云端技术盛会MongoDB.live,将于北京时间6月9日22:00正式开启,大会将以在线直播+按需学习相结合的方式,面向全球开发者.架构师等MongoDB 用户和爱好者免费开放,精 ...

  8. TODO:Linux安装PHP MongoDB驱动

    TODO:Linux安装PHP MongoDB驱动 PHP利于学习,使用广泛,主要适用于Web开发领域. MongoDB的主要目标是在键/值存储方式(提供了高性能和高度伸缩性)以及传统的RDBMS系统 ...

  9. thinkphp3.2 cli模式的正确使用方法

    最近要使用thinkphp3.2版本的cli模式,手动执的话没有问题,比如php /www/index.php home/article/get 这样没有问题,但是一般用cli模式都是定时任务比较多, ...

随机推荐

  1. fiddler手机端抓包配置

    首先,你得安装fiddler,这是前提条件,手机抓包有必须条件: 需要保持电脑和手机在同一个局域网中 (这一点,我一般会在电脑上启动一个wifi,然后手机连接即可) 下面说一下如何配置: 手机连接电脑 ...

  2. 汉诺塔的c++实现

    void hanNuoTa(int n,int a,int b,int c) { ) return; hanNuoTa(n - , a, c, b); cout << n << ...

  3. 详解UML图之类图 (转)

    原址: https://www.jianshu.com/p/4cd95d4ddb59 2.  怎么画类图?用什么工具? 使用工具:Visio或者processon在线作图 在类图中一共包含了以下几种模 ...

  4. vim没有权限却可以强制保存时所引起的思考 ------ 文件夹权限对所属文件的权限影响

    最近在拿着Linux 鸟叔私房菜对着Linux 系统学习一下基本操作,虽然已经使用Linux系统已经好多年不过却一直没有系统的学习一下.在用vim 编辑一个文件的时候出现了一个很神奇的事情,明明该文件 ...

  5. 每天一个linux命令(网络):【转载】ping命令

    Linux系统的ping命令是常用的网络命令,它通常用来测试与目标主机的连通性,我们经常会说“ping一下某机器,看是不是开着”.不能打开网页时会说“你先ping网关地址192.168.1.1试试”. ...

  6. SpringBoot启动报:Caused by: java.lang.IllegalArgumentException: At least one JPA metamodel must be present!

    使用spring boot对项目改造,启动报错: Caused by: java.lang.IllegalArgumentException: At least one JPA metamodel m ...

  7. ballerina 学习二十三 扩展ballerina

    扩展ballerina 目前有三种方式: 扩展client connector的包 (数据库访问,基础设施,api) 扩展server listenner 绑定为不同的协议 添加新的注解到baller ...

  8. string,char*及CString类型的相互转换

    首先先介绍一下什么是CString CString是MFC的字符串类,它不是基本类型,而是对字符串的封装,它是自适应的,在UNICODE环境下就是CStringW,在非UNICODE环境下就是CStr ...

  9. MAC OX 配置 Tomcat 说明

    1: 首先在官网下载 Tomcat(我选择的是最新的9.0) , http://tomcat.apache.org/ 2:下载完成之后将压缩包解压在/Library/下 可使用快捷键 control+ ...

  10. Window平台下React Native 开发环境搭建

    1. 安装Node.js 2. 安装react-native-cli 命令行工具 npm install -g react-nativew-cli 3. 创建项目 $ react-native ini ...