Node.js 事件循环是如何工作的?

9

在玩过 Node.js 并阅读关于异步 I/O 和事件驱动编程的资料后,我还是有些疑问。

考虑以下(伪)代码:

var http = require('http');

function onRequest(request, response)
{
    // some non-blocking db query
    query('SELECT name FROM users WHERE key=req.params['key']', function (err, results, fields) {
        if (err) {
            throw err;
        }
        username = results[0];
    });

    // some non-blocking db query
    query('SELECT name FROM events WHERE key=req.params['key']', function (err, results, fields) {
        if (err) {
            throw err;
        }
        event_name = results[0];
    });

    var body = renderView(username, event_name, template);
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.write(body);
    res.end();
};

http.createServer(onRequest).listen(8888);

// request A: http://127.0.0.1:1337/?key=A
// request B: http://127.0.0.1:1337/?key=B

我认为我理解事件循环的基础知识;使用libev,Node.js创建了一个事件循环,轮询(epoll/kqueue/...)一堆文件描述符以查看是否有任何事件被触发(新连接、可写、数据可用等)。如果有新请求,事件循环将调用传递给createServer的匿名函数。但我不明白的是之后会发生什么:

1)要并发运行查询,db驱动程序必须具有某种线程/连接池,对吧?

2)在一个请求的范围内:发送两个查询后会发生什么?renderView不能被调用,因为查询还没有返回。我们如何等待查询返回?它应该计算未完成回调的数量,然后再继续吗?我的基本想法是;

onRequest -> 运行异步代码 -> 等待回调 -> 构造响应。在这种情况下,等待是阻塞的,所以你实际上需要为每个onRequest生成一个线程。如何“等待回调运行后再构造响应”?

3)db驱动程序如何通知事件循环已经完成,并且需要调用其回调以获取查询结果?

4)事件循环如何运行我们使用onRequest事件创建的匿名函数中的回调?这是闭包概念介入的地方,上下文在回调函数中被“保存”吗?

5)现在我们有了db结果,如何继续执行renderView/res.write/res.end部分?


你知道代码是免费提供的,对吧?你有尝试阅读代码并找到瓶颈所在的地方吗? - jcolebrand
2个回答

6

运行并行异步代码模式:

要'等待两个异步函数的结果',您可以在两个异步调用回调中检查两个结果,如果都准备好了,就调用DoSomethingWithTwoDependantResults。

在您的示例中,可能需要按顺序执行查询:

query(sql1, function(sqlres1) {
    query(sql2, function(sqlres2) {
        writeResultUsingDataFrom(sqlres1, sqlres2);
    }
});

您的原始代码已修改,以便并行执行两个查询:

function writeReply(res, template, username, event_name)
{
    var body = renderView(username, event_name, template);
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.write(body);
    res.end();
} 

function onRequest(request, response)
{
    // some non-blocking db query
    query('SELECT name FROM users WHERE key=req.params['key']', function (err, results, fields) {
        if (err) {
            throw err;
        }
        username = results[0];
        if (username && event_name)
            writeReply(res, template, username, event_name);
    });

    // some non-blocking db query
    query('SELECT name FROM events WHERE key=req.params['key']', function (err, results, fields) {
        if (err) {
            throw err;
        }
        event_name = results[0];
        if (username && event_name)
            writeReply(res, template, username, event_name);

    });
};

3
你看过这个吗?我仍在努力理解其中的内容,无法详细回答你的问题,但基本上你对线程池的看法是正确的......Ryan在视频中解释了很多。
编辑:还有这个,大约一年之后发布,他在其中作了更详细的讲解。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接