将数据从数据库发送到没有模板引擎的客户端(node.js)

您好我正在与Node.js与Fulephp进行一些testing。 我有一个简单的设置,我试图看看更快。 我有一万个mogodb的logging被引入到视图中。 设置很简单,没有JS,没有CSS,最小的HTML。 我很快注意到,php的设置速度是原来的两倍。 起初,我认为nodejs比较慢,随着我的生活进行。 然而,我决定尝试没有玉的节点,我用它作为我的模板引擎,并且幸运地碰到了一个在stackoverflow上的post,这个背后的哲学并不是那么快,而是优雅。 然后我决定尝试没有任何临时的节点。 引擎。 但是,我意识到我不知道如何从数据库和节点传输数据到客户端,所以我很快就遇到了一个问题。 我进入了一个恐怖和绝望的漫漫长夜。 在某个时候,我得出结论说我需要socket.io的帮助。 虽然我能够连接到socket.io最终我仍然无法弄清楚如何传递数据。 然后我决定回去使用温度。 引擎,但是这次我决定尝试ejs。 最终,我能够渲染一些具有以下forms的数据[对象对象] ,但它不是一万条logging,更像是25.我决定做正确的事情,并在这里发表我的问题。 我想呈现没有模板引擎的视图,看我的假设是否正确。 毕竟我试图做两件事情,了解如何将数据传递给客户端表单node.js,看看它是否会提高我的performance。

这是我的app.js有一些意见:

/** * Mongo DB */ var mongous = require('mongous').Mongous, dbCollection = 'test.personnel'; /** * Module dependencies. */ var express = require('express'), app = module.exports = express.createServer(), pub = __dirname + '/public'; // Configuration app.configure(function(){ app.set('view options', {layout: false}); //not sure if i need these here, but left it in case app.use(app.router); app.use(express.static(pub)); }); //Simple templating //I took this example from stackoverflow, //can't find that post anymore, //though I can look if need be app.register('.html', { compile: function(str, options){ return function(locals){ return str; } } }); // Routes //This is where data is right now, it need to end up on the client side //This was working with jade and kinda worked with ejs (though I am not sure because I was getting [object Object]) //-------------------------------------------------- app.get('/', function(req, res){ mongous(dbCollection).find(function(output){ res.render('index.html',{ //the data is here, it works, i tested it with console.log data: output.documents }); }); }); //-------------------------------------------------- app.configure('production', function(){ app.use(express.errorHandler()); }); app.listen(3000); console.log('Express server listening on port %d in %s mode', app.address().port, app.settings.env); 

这是我的看法:

 <!DOCTYPE html> <html> <head> <meta charset='utf-8'> <title>Index</title> </head> <body> <div id="data"> Data needs to end up here. Somehow... </div> </body> </html> 

正如你所看到的那样。 现在我明白了,我很可能需要在客户端使用某种模板引擎,一旦我有了客户端的数据,我就可以自己完成。 其中最后可能会更慢,但我的主要目标是了解如何将node.js中的数据传递给客户端,以便我可以继续进行实验。 如果可以的话请帮忙,这样会大大提高我对节点的理解。 谢谢。

编辑:在你们所有人的帮助下,尤其是josh3736,这就是我最终的目标,如果你感兴趣… http://pastie.org/private/z3fjjbjff8284pr2mafw

正如你提到的答案一样,问题的一部分是模板引擎本身的速度。 你发现翡翠不是最快的 – 事实上, 这是最慢的一个 。

我最喜欢的引擎是doT 。 在我连接的性能testing中,doT可以每秒渲染540 次。 玉可以呈现一个类似的模板每秒只有29000次。 这甚至不是比赛。


但是,模板引擎速度只是这个问题的一小部分。 我相信你真正的问题是你正在使用的Mongo驱动程序似乎devise不佳的节点的asynchronous模型。 (声明:我从来没有真正使用过Mongous,只是花了几分钟的时间来看代码。)

节点旨在处理数据stream。 换句话说,你应该一次对非常小的数据块进行操作。 相反,它看起来像Mongous处理整个数据集并将其作为一个JSON对象返回给您的代码。

这对小数据集来说很方便,但是在处理大量数据(如10,000条logging)时会完全崩溃。 在parsing和处理那么多数据的时候,节点将被完全locking(这非常非常糟糕,因为它不能处理任何传入的连接),而V8内存pipe理系统没有针对大堆分配进行优化。

要正确处理大型数据集,您必须使用一个Mongo驱动程序,它将loggingstream式传输到您的代码中,例如node-mongodb-native或mongoskin ,这使得API更容易处理。

maerics'的答案是正确的,但是错误的是因为它使用了toArray ,这就产生了Mongous下的同样的问题: 整个数据集被整理成一个内存数组。 相反,只要使用游标的each方法,就可以为每个返回的loggingasynchronous调用callback函数。 这样,整个结果集永远不会在内存中; 你只能一个一个地工作,如果有必要的话,你已经处理的logging将被丢弃和垃圾收集。


现在我们已经build立了如何从数据库中获取数据,我们需要弄清楚如何获取数据到客户端。

这里的问题是Express视图系统希望你所有的数据都可以预先得到,这样模板引擎可以渲染出一个string发送给客户端。 正如我们上面所讨论的,如果你处理数千条logging,这不是一个好主意。 这样做的正确方法是将我们从Mongo获得的数据直接传输到客户端。 不幸的是,我们不能在Express视图内做到这一点 – 它们不是被devise成asynchronous的。

相反,你将不得不写一个自定义处理程序。 你已经在Hippo的回答和你自己的尝试的道路上,但你真正需要使用的是res.write() ,而不是res.send 。 和res.render一样, res.send希望在你调用它时有一个完整的响应,因为它在内部调用res.end ,结束HTTP响应。 相比之下, res.write仅仅通过networking发送数据,使得HTTP响应打开,并准备发送更多的数据 – 换句话说,stream式传输你的响应。 (请记住,在开始stream式传输之前,您必须设置任何HTTP标头,例如, res.contentType('text/html');

仅仅因为您手动处理响应(在视图渲染系统之前)并不妨碍您利用模板引擎。 您可以为文档的页眉和页脚使用模板,并为每个logging使用一个模板。 让我们使用doT把所有的东西放在一起。

首先,我们来声明我们的模板。 在现实生活中,你可能会从文件中加载这些文件(或者甚至是使用Express来加载它们作为视图并获取模板源代码),但是我们只是在代码中声明它们。

 var header = doT.template('<!DOCTYPE html><html><head><title>{{=it.title}}</title></head><body>'); var record = doT.template('<p>{{=it.fieldname}}, ...</p>'); var footer = doT.template('</body></html>'); 

doT.template返回一个函数,它会根据我们上面给出的模板生成HTML,并且在调用它时传递给返回函数的对象,例如,现在我们可以调用header({title:'Test'});

现在我们在请求处理程序中做实际的工作:(假设我们已经有了Mongo驱动程序的collection

 app.get('/', function(req, res){ res.contentType('text/html'); res.write(header({title:'Test'})); collection.find({}).each(function(err, doc) { if (err) return res.end('error! ' + err); // bail if there's an error if (doc) { res.write(record(doc)); } else { // `doc` will be null if we've reached the end of the resultset. res.end(footer()); } }); }); 

如果你坚持表示和某种数据库层,使用res.send()而不是res.render

另外请记住,node.js是相当新的,所以不是每个库都像其他语言一样稳定或快速。 (例如:您可能会更快地使用其他方法访问mongo,或者不使用express。)

那么这是我能够想出的最好的。 这当然是基于Hippos的答案,但有一点点东西。 但是,现在我正在做一些非常错误的事情,因为现在看起来比使用模板引擎慢得多, 慢得多

有问题的代码:

 //-------------------------------------------------- app.get('/', function(req, res){ mongous(dbCollection).find(10000,function(output){ var o = output.documents, str = JSON.stringify(o); res.send(str); }); }); //-------------------------------------------------- 

叹…

如果您对PHP和Node.js之间的真正原始性能testing感兴趣,那么您应该考虑使用MongoDB本地驱动程序和原始HTML将Node节点堆栈上的所有中间件(例如Express.js和Mongous)代。 像这样(未经testing):

 var http = require('http') , mongo = require('mongodb') , db = new mongo.Db('foo', new mongo.Server('localhost', 27017, {})); db.open(function(err, db) { if (err) throw err; db.collection('people', function(err, collection) { var server = http.createServer(function(req, res) { res.writeHead('200', {'Content-Type': 'text/html'}); res.write('<html><head><title>Node.js Output</title></head>'); res.write('<body><div id="data">'); collection.find(function(err, cursor) { cursor.each(function(err, doc) { if (doc) { res.write('<div>' + doc.name + '</div>'); } else { res.end('</div></body></html>'); cursor.close(); } }); }); }).listen(8080, 'localhost'); console.log('OK: listening on http://localhost:8080/'); }); }); 

[编辑]更新了mongo驱动程序的使用,以使用游标而不是“toArray”方法。

这里的一个关键select是Node MongoDB驱动程序; 特别是,你想要一个尽可能无阻塞的。 令人惊讶的是,现在似乎没有一个突出的(恕我直言)。