【问题标题】:Requests handling inside Event Pool using NodeJS使用 NodeJS 在事件池中处理请求
【发布时间】:2018-01-09 22:22:32
【问题描述】:

我已经阅读了多线程机制和NodeJS单线程机制here之间的区别。我对线程概念知之甚少。

我的问题

上面的文章说所有的Non Blocking I/O都是在Event loop中使用单线程处理的

我已经阅读了这个论坛上发布的问题,但它所说的只是单线程如何工作的概述,而不是更深层次的机制。说类似... 开始处理客户端请求 如果该客户端请求不需要任何阻塞 IO 操作,则处理所有内容,准备响应并将其发送回客户端。

如果事件队列中有 2 个或更多非阻塞请求,事件循环会获取每个请求并处理它。

第一个请求进入事件池开始处理,不等待或等待响应,同时请求2进入并开始处理,不等待。

现在,由于第二个请求已经使用线程进行处理(并且所有请求都使用单线程处理),目前正在处理第一个请求过程,如果有线程共享,它是如何发生的?

第一个请求进程在处理第二个请求时是否释放,然后又返回到第一个请求?如果是这样,从线程角度来看它是如何发生的?

单线程如何同时处理 2 个或更多请求,因为基本上线程将被分配给一个请求,直到它的所有进程完成

如何同时处理输入和输出操作的单线程?

有没有我想读的主题,所以我得到了这个单线程事件循环机制?

【问题讨论】:

    标签: javascript node.js multithreading asynchronous single-threaded


    【解决方案1】:

    首先,“单线程”仅适用于运行 Javascript 的一个线程。 node.js 有其他的本地线程来实现内置库中的一些功能。例如,文件 I/O 使用线程池来实现异步文件 I/O。但是,要了解您自己的 Javascript 是如何运行的,最重要的是您的 Javascript 只有一个线程。

    假设您有一个像这样的简单网络服务器:

    const http = require('http');
    
    function sendFile(res, filename) {
        if (filename.startsWith("/")) {
            filename = filename.slice(1) + ".html";
        }
        fs.readFile("1.html", (err, data) => {
           if (err) {
               res.writeHead(404);
               res.end("not found");
           } else {
               res.writeHead(200, {'Content-Type': 'text/html'});
               res.write(data);
               res.end();
           }
        });
    
    }
    
    const server = http.createServer((req, res) => {
        if (req.url === "/1" || req.url === "/2" || req.url === "/3") {
            sendFile(req.url);
        } else {
            res.writeHead(404);
            res.end("not found");
        }
    });
    
    server.listen(80);
    

    此 Web 服务器响应对三个 URL /1、/2 和 /3 的请求。

    现在假设三个独立的客户端分别请求其中一个 URL。以下是事件的顺序:

    1. 客户端A请求http://myserver.com/1
    2. 客户端B请求http://myserver.com/2
    3. 客户端C请求http://myserver.com/3
    4. 服务器接收来自客户端 A 的传入连接,建立连接,客户端发送 /1 请求,服务器读取并解析该请求。
    5. 当服务器忙于读取来自客户端 A 的请求时,来自客户端 B 和客户端 C 的请求都到达了。
    6. TCP 堆栈在操作系统级别处理传入连接(使用其他线程,即内核级别线程)。
    7. 到达连接的通知被放入 node.js 事件队列。因为 node.js 服务器正忙于为客户端 A 连接运行 Javascript,所以这两个事件现在位于事件队列中。
    8. 在其他连接到达的同时,node.js 服务器开始运行 /1 的请求处理程序。它在第一个if 语句中找到匹配项并调用sendFile("/1")
    9. sendFile() 调用 fs.readFile() 然后返回。因为fs.readFile()是异步的,所以文件操作开始了,但是交给了node.js内部的I/O系统,然后函数立即返回。当sendFile() 返回时,它会返回到 http 服务器请求处理程序,然后该处理程序也会返回。在这一点上,这个请求没有其他事情要做。控制权已交还给解释器以决定下一步该做什么。
    10. node.js 解释器检查事件队列以查看其中是否有任何要处理的内容。它找到来自客户端 B 的传入请求,并且该请求开始处理。此请求会经历相同的 8 和 9 步骤,直到它返回并启动另一个 fs.readFile() 操作。
    11. 然后对来自客户端 C 的传入请求重复第 10 步。
    12. 然后,不久之后,之前启动的三个 fs.readfile() 操作之一完成并将完成回调放入 Javascript 事件队列。一旦 Javascript 解释器无事可做,它就会在事件队列中找到该事件并开始处理它。这将调用传递给 fs.readFile() 的回调,并使用该函数期望的两个参数,并且回调中的代码开始执行。
    13. 假设fs.readFile() 操作成功,它会调用res.writeHead(),然后是res.write(),然后是res.send()。这三个调用都将数据发送到底层操作系统 TCP 堆栈,然后将其发送回客户端。
    14. res.end() 返回后,控制权返回解释器并检查事件队列中的下一个事件。如果另一个fs.readFile() 回调已经在事件队列中,那么它将被拉出事件队列并像前一个一样处理。如果事件队列为空,则解释器会一直等待,直到将某些内容放入事件队列。

    如果事件队列中有 2 个或更多非阻塞请求,事件循环会获取每个请求并处理它。

    node.js 一次只运行一个。但是,关键是请求处理程序中的异步代码允许处理程序将控制权返回给系统,以便在第一个请求等待其异步操作完成时可以处理其他事件。这是一种合作的、非先发制人的多任务处理形式。它不是 Javascript 的多个线程。第一个请求处理程序实际上开始并进行异步操作,然后返回(就好像它已经完成一样)。当它返回时,队列中的下一个事件可以开始处理。稍后,当异步操作完成时,它会将自己的事件插入到事件队列中,然后重新排队使用 Javascript 的单线程。

    第一个请求进入事件池开始处理,不等待或等待响应,同时请求2进入并开始处理,不等待。

    上面已经描述了大部分内容。如果 Javascript 线程忙,为什么请求 2 进入事件队列,该请求将位于事件队列中,直到 Javascript 线程不再忙。它可能需要等待很短的时间。但是,它不必等到请求 1 完成,只需等到请求 1 将控制权返回给系统,并且它本身正在等待某个异步操作完成。

    现在,由于第二个请求已经使用线程进行处理(并且所有请求都使用单线程处理),目前正在处理第一个请求过程,如果有线程共享,它是如何发生的?

    当第二个请求使用 Javascript 线程时,第一个请求没有运行任何 Javascript。它的本机代码异步操作可能在后台运行(所有异步操作都需要一些本机代码),但是在任何给定时间只有一个 Javascript 运行,所以如果第二个请求正在运行一些 Javascript,那么第一个请求是等待其异步操作完成,或者该操作已经完成,并且一个事件位于事件队列中,等待第二个请求完成,以便可以处理该事件。

    在处理第二个请求时,第一个请求进程是否被释放,然后又返回到第一个请求?如果是这样,它在线程透视图中是如何发生的?

    这一切都通过事件队列进行。第一个请求运行直到它返回。第二个请求运行直到它返回。当来自第一个请求的异步操作完成时,它会在事件队列中插入一个项目。当 JS 解释器空闲时,它会从事件队列中拉出该事件并运行它。异步操作的原生代码实现可能会涉及线程,但Javascript的线程仍然只有一个。

    单线程如何同时处理 2 个或更多请求,因为基本上线程将被分配给一个请求,直到它的所有进程完成

    它实际上从未同时运行多个 Javascript。每个不同操作的 Javascript 都会运行,直到将控制权返回给解释器。异步操作(例如文件 I/O 或网络操作)可以并发运行,并且由本机代码管理,有时使用额外的线程,有时不使用。文件 I/O 使用线程池来实现非阻塞、异步文件 I/O。网络使用操作系统事件通知(select、epoll 等),而不是线程。

    如何同时处理输入和输出操作的单线程?

    它不在您的 Javascript 中。它通常会读取,然后写入。它不会“同时”做两者。现在,TCP 堆栈可能在操作系统内部做一些实际的并行工作,但这一切都由操作系统管理,甚至可能在某些时候在网络接口上被序列化。请求由单线程处理,其中管理输入输出进程由操作系统为每个进程创建的操作系统级线程

    有没有什么我想念的主题,所以我得到了这个单线程事件循环机制?

    阅读所有关于 Javascript 事件队列的信息。以下是一些帮助您入门的参考资料:

    How does JavaScript handle AJAX responses in the background?

    Where is the node.js event queue?

    Node.js server with multiple concurrent requests, how does it work?

    Asynchronous process handler in node

    How does a single thread handle asynchronous code in Node.js?

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-10-04
      • 2015-09-24
      • 2023-03-18
      • 1970-01-01
      • 1970-01-01
      • 2016-08-04
      • 1970-01-01
      相关资源
      最近更新 更多