【问题标题】:Send data from database to client without templating engine (node.js)无需模板引擎(node.js)将数据从数据库发送到客户端
【发布时间】:2012-02-05 22:38:27
【问题描述】:

您好,我正在使用 Node.js 与 Fulephp 进行一些测试。我有一个简单的设置,我试图看看什么更快。我在 mogodb 中有 1 万条记录被拉入视图。设置很简单,没有 js,没有 css,只有最少的 html。我很快注意到 php 设置的速度是原来的两倍。起初,我认为 nodejs 速度较慢,然后继续我的生活。然而,我决定尝试使用我用作模板引擎的不带翡翠的节点,幸运的是,我在 stackoverflow 上发现了一篇关于翡翠背后的哲学不是速度而是优雅。然后我决定尝试没有任何临时节点的节点。引擎。但是我很快就遇到了一个问题,因为我意识到我不知道如何将数据从数据库和节点传递到客户端。我在恐惧和绝望中度过了一个漫长的夜晚。在某个时候,我得出一个结论,即我需要 socket.io 的帮助。虽然我最终能够连接到 socket.io,但我仍然无法弄清楚如何传递数据。然后我决定回到使用温度计。引擎,但这次我决定尝试ejs。最终,我能够呈现一些具有以下形式的数据 [object Object],但它不是 10000 条记录,更像是 25 条。我决定做正确的事情并在这里发布我的问题。我想在没有模板引擎的情况下渲染视图,看看我的假设是否正确。 毕竟我正在尝试做两件事,了解如何将数据传递到客户端表单 node.js 并查看它是否会提高我的性能。

这是我的 app.js 和一些 cmets:

/**
 * 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

【问题讨论】:

    标签: javascript node.js


    【解决方案1】:

    正如您在回答中提到的那样,部分问题在于模板引擎本身的速度;你发现 Jade 并不是最快的——事实上,it's one of the slowest

    我最喜欢的引擎是doT。在我链接的performance test 中,doT 每秒可以渲染模板 5.4 百万次。 Jade 每秒只能渲染类似的模板 29,000 次。这甚至不是比赛。


    但是,模板引擎速度只是这里问题的一小部分。我相信您真正的问题是您使用的 Mongo 驱动程序似乎针对 Node 的异步模型设计得很差。 (免责声明:我从未真正使用过 Mongous;我只是花了几分钟查看代码。)

    Node 用于处理数据流。换句话说,您应该一次对非常小的数据块进行操作。相比之下,Mongous 似乎处理了整个数据集,并将其作为一个 JSON 对象返回给您的代码。

    这对于小型数据集来说既方便又好,但在处理像您这样的大量数据(10,000 条记录)时就完全崩溃了。在解析和处理大量数据时,节点将被完全锁定(这非常非常糟糕,因为它无法处理任何传入的连接),并且 V8 内存管理系统没有针对像这样的大型堆分配进行优化.

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

    maerics 的答案在正确的轨道上,但是是错误的,因为它使用了 toArray,这会产生与 Mongous 下相同的问题:整个 数据集被整理到内存中的数组中.相反,只需使用光标的each 方法,该方法为每个返回的记录异步调用您的回调函数当它进入时。这样,整个结果集就永远不会在内存中;您一次只处理一个,允许丢弃您已经处理的记录并在必要时收集垃圾。


    现在我们已经确定了如何将您的数据从数据库中取出,我们需要弄清楚如何将其发送给客户端。

    这里的问题是 Express 的视图系统希望您预先准备好所有数据,以便模板引擎可以渲染出单个字符串以发送给客户端。正如我们上面所讨论的,如果您要处理数千条记录,这不是一个好主意。正确的做法是将我们从 Mongo 获得的数据直接流式传输到客户端。不幸的是,我们不能在 Express 视图中真正做到这一点——它们并非设计为异步的。

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

    仅仅因为您手动处理响应(在视图呈现系统之前)并不妨碍您利用模板引擎。您可以为文档的页眉和页脚使用一个模板,为每条记录使用一个模板。让我们使用 doT 将所有内容放在一起。

    首先,让我们声明我们的模板。在现实生活中,您可能会从文件中加载这些(或者甚至是 hack 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());
            }
        });
    });
    

    【讨论】:

    • 另外,另一个教训:仅仅因为某人 posts GitHub 上的某些代码并不一定意味着它是好的或精心设计的代码。
    • 哇,这是我在 stackoverflow 上收到的最长的回答。它似乎如此深入和彻底,我只想将其标记为正确,但我会花时间阅读它。我浏览了一下,到目前为止,我喜欢我所看到的,这是我所希望的答案。我会试试你添加的内容,看看效果如何。我实际上已经看到了不同模板引擎的测试,并想尝试快速的。无论如何,感谢您的回答,我需要一些时间来完成它并尝试一下。谢谢。
    • 我能够按照您所说的进行操作,但不幸的是现在加载速度更慢了。我唯一的结论是我仍然在某个地方阻塞。尽管如此,我发布这个问题并不是为了获得最快的结果,而是为了了解事物是如何在节点中移动的。至少我理解得更好,我发布了代码,如果你有时间或愿意的话,你可以帮我指出什么是阻塞的。但无论如何谢谢。我实现了我的目标,那就是提高我的理解力。我仍然会研究 express hack 并通过视图而不是 app.js 推送所有代码。谢谢。
    • 也许你也可以帮我解决这个问题? stackoverflow.com/questions/9252191/…
    【解决方案2】:

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

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

    【讨论】:

    • 是的,我明白你的意思。我有一个问题,但如果我使用 res.send() ,并且如果我在客户端执行 console.log(data) ,数据是否会显示在客户端,因为这是我缺少的关键部分:是如何使数据显示在客户端。但是感谢您的回复,我会研究您所说的并尝试使其发挥作用。谢谢。
    • 如果您要处理大量记录,您实际上想使用res.write(而不是res.send),原因我在my answer 中描述。
    【解决方案3】:

    嗯,这是我能想到的最好的了。它当然是基于 Hippos 的回答,但有更多的东西。但是,现在我做错了,因为现在它似乎比使用模板引擎还要慢,慢得多...

    有问题的代码:

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

    叹息...

    【讨论】:

      【解决方案4】:

      如果您对 PHP 和 Node.js 之间的真正原始性能测试感兴趣,那么您应该考虑放弃 Node 堆栈上的所有中间件(例如 Express.js 和 Mongous),使用 MongoDB native driver 直接方法和原始 HTML 生成。像这样的东西(未经测试):

      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”方法。

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

      【讨论】:

      • 我不会说我对原始表演感兴趣,因为我使用的是框架框架。所以我假设如果我只保留快递,那将是公平的。虽然我可以穿。不过,谢谢你的回答,好像也不是很复杂。我在网上看到过类似的例子,虽然没有 mongo db。就像我说的,我想至少保持表达,在这一点上与其说是表演练习,不如说是理解练习。
      • 您真的想使用cursor.each() 而不是cursor.toArray(),原因我在my answer 中描述。
      • @josh3736:对,我的示例代码只是为了演示;你的回答肯定更彻底。
      猜你喜欢
      • 2022-01-09
      • 1970-01-01
      • 2014-10-11
      • 1970-01-01
      • 2021-11-03
      • 2013-02-01
      • 2017-05-07
      • 2021-12-27
      • 1970-01-01
      相关资源
      最近更新 更多