热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

MongoDBJavaScriptDriver驱动的使用示例

下面文章是对MongoDB的几个Node.js客户端的评测,在评测后,作者虽然没有选择MongoDB+Node.js的组合,但是其测试过程和结果具有参考意义,在故障情况下的测试,确实也是我们选择使用每一种工具时必要的一环。如果你在使用MongoDB,你对下面说的情况做过

下面文章是对 MongoDB 的几个 Node.js 客户端的评测,在评测后,作者虽然没有选择 MongoDB+Node.js 的组合,但是其测试过程和结果具有参考意义,在故障情况下的测试,确实也是我们选择使用每一种工具时必要的一环。如果你在使用MongoDB,你对下面说的情况做过测试吗?

本文来源于作者@latteye 的热心投搞。原文链接:latteye.com。

本站欢迎各种NoSQL方面的新闻技术文章投稿,下面是原文。

在对 node.js + MongoDB 做了一周不到的测试之后,我们决定放弃这对组合。放弃的原因有二:

MongoDB 对数据的保障性不是我们所需要的。这不是 MongoDB 的错误,这是我们选择产品的错误。我觉得 MongoDB 其实就是放弃了这样的数据保障性才获得了更好的性能。所以才更适合类似 facebook twitter 对消息保障性要求不高,但是量大的应用。

Javascript 的 driver 略显不成熟。其实各类开发速度都很快,同时我对他们的熟悉程度还不够好。所以总的感觉现在还没到用的时候。

这里对第二点做个流水账式样的记录,在学习的过程中发现相关的英文和中文资料都比较缺乏。

我所测试到的 Driver 有:

这三个 Driver 里,mongolian 和 mongoose 都是依赖 native 的。不过在这里mongolian的作者提到 mongolian对 native db class 部分并不调用。看来依赖的程度有所不一。

所测试的内容是 failover。MongoDB 推荐的 failover 方案为 Replica Set,这个架构逻辑上不难理解。至少三个节点,至多七个节点;各个节点可以有 0-99 的优先级等一系列特性让他成为非常优秀的 HA 方案。

测试方法: 插入 N 条数据,并且在插入的过程中将 Primary 进程杀死。查看客户端(node.js)是否正常转移到新的 Primary ,并且最终检查数据一致性。可以接受插入不了数据,但是一定要有错误返回。返回错误的数量一定要和数据库内未插入的数据数量一致。

一、native

先给出 native 的测试脚本:

var mongodb = require('mongodb');
var Db = require('mongodb').Db,
  Connection = require('mongodb').Connection,
  Server = require('mongodb').Server,
  ReplSetServers = require('mongodb').ReplSetServers;
var replStat = new ReplSetServers([
        new Server('172.16.5.151', 28010, { auto_reconnect: true }),
        new Server('172.16.5.152', 28010, { auto_reconnect: true }),
        new Server('172.16.5.153', 28010, { auto_reconnect: true })
        ],
        {rs_name: 'rs1'}
);
var db = new Db('a', replStat);
db.open(function (error, client) {
  if (error) throw error;
  var collection = new mongodb.Collection(client, 'blogposts');
function test_read(t)
{
  console.log('enum elements...');
  var start = new Date;
  var times = 0;
  for(var i = 1; i <= t; i++ )
  {
        collection.find({'_id':i}, {limit:1}).nextObject(function(err, docs) {
            if (err) console.warn(err.message);
            //else console.dir(docs);
            if(++times >= t)
                console.log('enum finished:cost time:' + (new Date - start) + 'ms');
          });
  }
}
function test_write(t)
{
  var start = new Date;
  var times = 0;
  console.log('add elements...');
  for(var i = 1; i <= t; i++ )
  {
        collection.insert({date: (new Date()).getTime(), body:'sadf', title:'abc', _id:i}, {safe:{w:2, wtimeout: 10000}},
                    function(err, objects) {
            if (err) console.warn(err.message);
            if(++times >= t)
            {
                console.log('add finished:cost time:' + (new Date - start) + 'ms');
                test_read(t);
            }
        });
  }
}
var wtimes = 10000;
test_write(wtimes);
//test_read(wtimes);
});

三个 driver 中文档工作做的最好的就是 native 了,example 也比较多。不过作者在 Replica Set 的 examples 中给了个让人很莫名的开头:

var port1 = 27018;
var port2 = 27019;
var server = new Server(host, port, {});
var server1 = new Server(host, port1, {});
var server2 = new Server(host, port2, {});
var servers = new Array();
servers[0] = server2;
servers[1] = server1;
servers[2] = server;
var replStat = new ReplSetServers(servers);

对于我这种不写代码的人来说,您老写成这样着实让我纠结了一番

测试结果: 在插入的过程中将 Primary kill 后大约有 1/3 的概率 node.js crash 了。其余 2/3 的概率 node.js 彻底卡住。MongoDB 端 Primary 正常转移,但未见数据继续插入进来。我很想贴一点 log 上来,但 native driver 真的没有任何 log,就是单纯的卡住了卡住卡

Crash log:

[root@localhost bin]# ./node ~/native_test.js
2
3
4
add elements...
node: src/uv-common.c:92: uv_err_name: Assertion `0' failed.
已放弃

在经过几天的搜索以后[1 2 3 ] 我发现似乎有人和我做过类似的测试,但是从来没有得到明确的答案。昨天我也将这个问题发到的 native 论坛上,目前还没有人回复。
但是后来又随后开始怀疑自己的脚本,同时看到新的解答[4],于是开始尝试不在一个 db.open 里面写 for,而在 for 里面反复的 db.open 和 db.close。但是没有成功,循环插入10条数据,成功插入的只有第一条。无论有没有 db.close 都是这个现象。这个不工作的代码就不贴上来了,如果有那位做过类似测试希望可以交流一下。

二、mongoose

测试脚本:

var mongoose = require('mongoose');
mongoose.createSetConnection('mongodb://172.16.5.151:28010/a,mongodb://172.16.5.152:28010/a,mongodb://172.16.5.153:28010/a');
var Schema = mongoose.Schema,
    ObjectId = Schema.ObjectId;
var BlogPost = new Schema({
//    author    : ObjectId
    _id       : Number
  , title     : String
  , body      : String
  , date      : Date
});
mongoose.model('BlogPost', BlogPost);
var post = mongoose.model('BlogPost');
function test_read(t)
{
        var start = new Date();
        var times = 0;
        console.log('enum elements...');
        for(var i = 1; i <= t; i++)
        {
                //console.log("read:"+i);
                post.findById(i, function(err, doc){
                        if(err)
                                console.log(err);
                        //else
                        //      console.log(doc);
                        if(++times >= t)
                        {
                                var end = new Date();
                                console.log('enum finished:cost time:' + (end - start) + "ms");
                        }
                });
        }
};
function test_write(t)
{
        var start = new Date();
        var times = 0;
        console.log('add elements...');
        for(var i = 1; i <= t; i++)
        {
                //console.log("write:"+i);
                var p = new post();
                p._id = i;
                p.title = 'abc';
                p.body = 'sadf';
                p.date = (new Date()).getTime();
                p.save(function(err){
                        if(err)
                        {
                                console.log(err);
                        }
                        if(++times >= t)
                        {
                                var end = new Date();
                                console.log('add finished:cost time:' + (end - start) + "ms");
                                test_read(t);
                        }
                });
        }
}
var wtimes = 10000;
test_write(wtimes);
//process.exit(0);

首先!连接 Replica Set 要用createSetConnection:

mongoose.createSetConnection('mongodb://172.16.5.151:28010/a,mongodb://172.16.5.152:28010/a,mongodb://172.16.5.153:28010/a');

你或许和我一样走过一些弯路[5 6]。

测试结果: OSE 的测试结果几乎和 native 一样,唯一好一点的是它从来没把 node.js 弄 crash 过。它唯一的反应就是 卡住卡住
OSE 和 native 在这个测试上的区别是,native 一边产生数据一边插入。OSE 先将数据在内存中产生出来以后,再一次插入数据库。而 node.js 存在一个内存限制的问题 (一个浏览器有什么理由需要2G的内存呢?),所以当 OSE driver 占用超过 1.9G 内存之后,node.js 不出意料的 crash。

PS. google 论坛上有人说可以通过参数让 node.js 支持任何大小的内存。经过我的测试(CentOS 6 x86-64,0.5.x,0.4.x)没有成功过。可工作的最高数值为 1900M。

你可以注意到了 native 驱动有一个 auto_reconnect 参数(尽管它没有 reconnect),而 mongoose 脚本里面没看到。OSE 的确也有设置 auto_reconnect 的方式[7],但是只看到给普通连接设置的方式。没有看到给 Replica Set 用的方式。自己胡乱尝试了几个设置方式无一成功。希望 ose 的作者能再多花点时间在文档方面。另一方面也可以看到,OSE 其实对 native 依赖还是蛮严重的。这种设置方式的出现似乎只是传递给 native 驱动,我猜测 OSE 自己没有对这块做任何处理。

三、mongolian

测试脚本:

var mongodb = require('mongolian');
var server = new mongodb(
    "172.16.5.151:28010",
    "172.16.5.152:28010",
    "172.16.5.153:28010"
)
var db = server.db("a")
var blogposts = db.collection("blogposts")
function test_read(t)
{
  console.log('enum elements...');
  var start = new Date;
  var times = 0;
  for(var i = 1; i <= t; i++ )
  {
        blogposts.find({'_id':i}, {limit:1}).nextObject(function(err, docs) {
            if (err) console.warn(err.message);
            //else console.dir(docs);
            if(++times >= t)
                console.log('enum finished:cost time:' + (new Date - start) + 'ms');
          });
  }
}
function test_write(t)
{
  var start = new Date;
  var times = 0;
  console.log('add elements...');
  for(var i = 1; i <= t; i++ )
  {
        blogposts.insert({date: (new Date()).getTime(), body:'sadf', title:'abc', _id:i},
                    function(err, objects) {
            if (err) console.warn(err.message);
            if(++times >= t)
            {
                console.log('add finished:cost time:' + (new Date - start) + 'ms');
                test_read(t);
            }
        });
  }
}
var wtimes = 10000;
test_write(wtimes);
//test_read(wtimes);

测试结果: mongolian(以下简称lian) 的反应是这三个驱动中最好的。首先当开启 node 的时候,lian 会给出 debug 信息,明确告诉你他连接到了哪台 mongodb,作者也明确说了这个 log 是为 Replica Set 做的 [89]。当 Primary 被 kill 掉之后,lian 会告诉你连接丢失。在后面的插入lian会明确的告诉你插入失败,并且是每一次插入就给出一个 log,而且程序会一路走下去,不会卡住。

[root@localhost ~]# node lian_test.js
add elements...
[debug] mongo://172.16.5.151:28010: Disconnected
[error] mongo://172.16.5.151:28010: Error: ECONNREFUSED, Connection refused
[debug] mongo://172.16.5.152:28010: Connected
[debug] mongo://172.16.5.153:28010: Connected
[debug] mongo://172.16.5.152:28010: Initialized as secondary
[debug] mongo://172.16.5.153:28010: Initialized as primary
[info] mongo://172.16.5.153:28010: Connected to primary
[debug] Finished scanning... primary? mongo://172.16.5.153:28010

我觉得 lian 的这种工作模式可以从它的代码编写方式里面体现出来。lian 的代码里面不存在打开一个 connection 或者 db.open 这样的概念,所以我估计 lian 是每一次 insert 就会尝试打开一次 connection。虽然他没有再次找到正确的 Primary,但至少他知道自己连接丢失了。
但是 lian 没能再次找到正确的 Primary 可能意味着他先打开了一个 ConnectionPool (你可以通过 poolSize 在 native 里面设置 pool 的大小),只有打开 ConnectionPool 的时候才会尝试去做 Primary 判断。
另外 lian 的插入速度也不错,感觉比 OSE 好,几乎和 native 一样。

实验做到后面,我极度怀疑自己的测试脚本写的不对。因为 native 是有 auto_reconnect 的参数的,但是缺没有工作。作者应该考虑了这个问题的。
而也肯定有一种方式让我在 for 里面打开 connection 、写完、关闭 connection。只是我现在没找到正确的写法。
希望有经验的朋友给予一些帮助。


推荐阅读
  • 在CentOS 7环境中安装配置Redis及使用Redis Desktop Manager连接时的注意事项与技巧
    在 CentOS 7 环境中安装和配置 Redis 时,需要注意一些关键步骤和最佳实践。本文详细介绍了从安装 Redis 到配置其基本参数的全过程,并提供了使用 Redis Desktop Manager 连接 Redis 服务器的技巧和注意事项。此外,还探讨了如何优化性能和确保数据安全,帮助用户在生产环境中高效地管理和使用 Redis。 ... [详细]
  • 本文介绍了如何使用Node.js通过两种不同的方法连接MongoDB数据库,包括使用MongoClient对象和连接字符串的方法。每种方法都有其特点和适用场景,适合不同需求的开发者。 ... [详细]
  • 本文探讨了使用lightopenid库实现网站登录,并在用户成功登录后,如何获取其姓名、电子邮件及出生日期等详细信息的方法。特别针对Google OpenID进行了说明。 ... [详细]
  • 实践指南:使用Express、Create React App与MongoDB搭建React开发环境
    本文详细介绍了如何利用Express、Create React App和MongoDB构建一个高效的React应用开发环境,旨在为开发者提供一套完整的解决方案,包括环境搭建、数据模拟及前后端交互。 ... [详细]
  • MongoDB核心概念详解
    本文介绍了NoSQL数据库的概念及其应用场景,重点解析了MongoDB的基本特性、数据结构以及常用操作。MongoDB是一个高性能、高可用且易于扩展的文档数据库系统。 ... [详细]
  • V8不仅是一款著名的八缸发动机,广泛应用于道奇Charger、宾利Continental GT和BossHoss摩托车中。自2008年以来,作为Chromium项目的一部分,V8 JavaScript引擎在性能优化和技术创新方面取得了显著进展。该引擎通过先进的编译技术和高效的垃圾回收机制,显著提升了JavaScript的执行效率,为现代Web应用提供了强大的支持。持续的优化和创新使得V8在处理复杂计算和大规模数据时表现更加出色,成为众多开发者和企业的首选。 ... [详细]
  • TypeScript 实战分享:Google 工程师深度解析 TypeScript 开发经验与心得
    TypeScript 实战分享:Google 工程师深度解析 TypeScript 开发经验与心得 ... [详细]
  • 作为140字符的开创者,Twitter看似简单却异常复杂。其简洁之处在于仅用140个字符就能实现信息的高效传播,甚至在多次全球性事件中超越传统媒体的速度。然而,为了支持2亿用户的高效使用,其背后的技术架构和系统设计则极为复杂,涉及高并发处理、数据存储和实时传输等多个技术挑战。 ... [详细]
  • ArcBlock 发布 ABT 节点 1.0.31 版本更新
    2020年11月9日,ArcBlock 区块链基础平台发布了 ABT 节点开发平台的1.0.31版本更新,此次更新带来了多项功能增强与性能优化。 ... [详细]
  • 在Eclipse中提升开发效率,推荐使用Google V8插件以增强Node.js的调试体验。安装方法有两种:一是通过Eclipse Marketplace搜索并安装;二是通过“Help”菜单中的“Install New Software”,在名称栏输入“googleV8”。此插件能够显著改善调试过程中的性能和响应速度,提高开发者的生产力。 ... [详细]
  • C++ 异步编程中获取线程执行结果的方法与技巧及其在前端开发中的应用探讨
    本文探讨了C++异步编程中获取线程执行结果的方法与技巧,并深入分析了这些技术在前端开发中的应用。通过对比不同的异步编程模型,本文详细介绍了如何高效地处理多线程任务,确保程序的稳定性和性能。同时,文章还结合实际案例,展示了这些方法在前端异步编程中的具体实现和优化策略。 ... [详细]
  • 在处理木偶评估函数时,我发现可以顺利传递本机对象(如字符串、列表和数字),但每当尝试将JSHandle或ElementHandle作为参数传递时,函数会拒绝接受这些对象。这可能是由于这些句柄对象的特殊性质导致的,建议在使用时进行适当的转换或封装,以确保函数能够正确处理。 ... [详细]
  • Node.js 配置文件管理方法详解与最佳实践
    本文详细介绍了 Node.js 中配置文件管理的方法与最佳实践,涵盖常见的配置文件格式及其优缺点,并提供了多种实用技巧和示例代码,帮助开发者高效地管理和维护项目配置,具有较高的参考价值。 ... [详细]
  • 字节码开发笔记:深入解析与应用技巧 ... [详细]
  • 今日精选:10款实用的jQuery随机效果插件
    在今天的精选内容中,我们推荐了10款实用的jQuery随机效果插件。这些插件不仅功能强大,而且设计精良,能够为您的网页增添独特的互动体验。从动态图像效果到文本动画,每款插件都提供了丰富的自定义选项,帮助开发者轻松实现创意视觉效果。特别值得一提的是,其中一款插件集成了与Google API的无缝对接,使数据展示更加生动和直观。 ... [详细]
author-avatar
夏石兵
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有