【问题标题】:Understanding The NodeJS Internal execution了解 NodeJS 内部执行
【发布时间】:2016-12-05 18:38:59
【问题描述】:

我试图了解幕后发生的事情 如果我尝试执行这个 NodeJS 代码:

http.createServer(function (request, response) { response.writeHead(200, {'Content-Type': 'text/plain'}); response.end('Hello World\n'); }).listen(8081);

我有2个关于上述代码的案例:

1 .修改代码在最后一行做一些阻塞 http.createServer 回调函数:

http.createServer(function (request, response) {    
    response.writeHead(200, {'Content-Type': 'text/plain'});

    response.end('Hello World\n');

    sleep(2000); //sleep 2 seconds after handling the first request 

}).listen(8081);`


//found this code on the web, to simulate php like sleep function 

function sleep(milliseconds) 
{
   var start = new Date().getTime();
   for (var i = 0; i < 1e7; i++) 
   {
      if ((new Date().getTime() - start) > milliseconds)
      {
          break;
      }
   }
}

我使用这个简单的 bash 循环向 NodeJS 服务器发出两个请求

$for i in {1..2}; do curl http://localhost:1337; done

客户端控制台上的结果:

Hello world#第一次迭代

两秒钟后,客户端控制台上会打印下一个 hello world

Hello world#第二次迭代

在请求的第一次迭代中,服务器可以立即响应请求。 但是在请求的第二次迭代中,服务器处于阻塞状态,并在两秒后返回对请求的响应。这是因为睡眠 处理第一个请求后阻塞请求的函数。

  1. 修改代码,而不是使用睡眠,我在http.createServer回调函数的最后一行使用setTimeout

    http.createServer(function (request, response) {    
        response.writeHead(200, {'Content-Type': 'text/plain'});
    
        response.end('Hello World\n');
    
        setTimeout(function(){console.log("Done");}, 2000);
    
     }).listen(8081);`
    

我再次使用这个简单的 bash 循环来执行请求

for i in {1..2}; do curl http://localhost:1337; done

结果是响应立即返回给两个请求。 Hello world 消息也会立即打印在控制台上。 这是因为我使用的是 setTimeout 函数,它本身就是一个异步函数。

我对这里发生的事情有疑问:

1.我说对了吗:It is the responsibility for the programmer to make asynchronous call in NodeJS code so that the NodeJS internal can continue to execute other code or request without blocking.

2.NodeJS 内部使用Google V8 Engine 执行javascript 代码,使用libuv 执行异步操作。

事件循环负责检查事件队列中是否发生与回调相关的事件,并检查调用堆栈中是否有任何剩余代码,如果事件队列不为空且调用堆栈为空,则从事件回调队列被压入栈,导致回调被执行。

问题是:

A.在 NodeJS 中做异步事情时,回调函数的执行是否与 NodeJS 主线程中的代码执行分开(通过使用libuv 线程池)?

B.如果有多个连接同时到达服务器,Event Loop如何处理连接?

我会非常感谢每一个答案并尝试向他们学习。

【问题讨论】:

    标签: node.js multithreading event-loop


    【解决方案1】:

    关于你的几个问题:

    1. 进行异步调用是程序员的责任 在 NodeJS 代码中,以便 NodeJS 内部可以继续执行 其他代码或请求没有阻塞。

    正确!请注意,可以(如果需要)执行同步阻塞代码。例如查看 fs 模块的所有“同步”功能,如 fs.accessSync

    1. 在 NodeJS 中做 Async 的时候,是回调的执行吗 函数与执行分离(通过使用 libuv 线程池) NodeJS主线程中的代码

    Node.js 是单线程的,所以没有“主线程”。触发时,回调函数的执行是唯一执行的代码。正如你提到的,node.js 的异步设计是通过“事件循环”完成的

    1. 如果有多个连接,事件循环如何处理连接 连接同时到达服务器?

    真的没有“同一时间”。一个先到,其余的都在排队。假设你没有阻塞代码,它们应该被快速处理(你可以并且应该对你的服务器进行负载测试,看看到底有多快)

    【讨论】:

    【解决方案2】:

    首先,我不知道 sleep 有什么作用。

    基本上,事件循环会检查哪些资源是空闲的,以及排队事件的需求(如果有)。当您调用 setTimeout 时,它会在 2 秒后执行 console.log("Done")。您是否对其进行了编程以停止该功能的整体执行? 。您只是在发送响应后才要求该特定请求执行某些操作。您没有要求停止函数执行或阻止事件。您可以阅读更多关于线程here 的信息。该程序本身是异步的。

    现在如果你想让它同步,你需要你自己的事件循环。你能在 setTimeout 中执行所有操作吗?

    setTimeout(function() {
        response.end('Hello World\n');
        response.writeHead(200, {'Content-Type': 'text/plain'});
        console.log("Done");
    }, 2000);
    

    您仍然拒绝其他停止执行的请求吗? 。如果您同时发出 2 个请求,您将在 2 秒后同时收到 2 个响应。

    让我们更深入地控制请求。假设有两个全局变量 counter = 0 和 current_counter = 0。它们位于 http.create... 之外。一旦请求到来,我们就为其分配一个计数器并执行它。然后我们等待 2 秒,然后递增计数器并执行下一个请求。

    counter = 0;
    current_counter = 0;
    http.createServer(function (request, response) {
        var my_count = counter; // my_count specific to each request, not common, not global
        counter += 1;
    
        while(current_counter <= my_count)
            if (current_counter == my_count) {
                setTimeout(function() {
                    response.end('Hello World\n');
                    response.writeHead(200, {'Content-Type': 'text/plain'});
                    console.log("Done");
                    return current_counter += 1;
                }, 2000);
            }
        }
    }).listen(8081);`
    

    试着理解我做了什么。我以while循环的形式制作了自己的事件循环。它监听 current_counter 等于 my_count 的条件。想象一下,3 个请求在不到 2 秒的时间内到达。请记住,我们仅在 2 秒后递增 current_counter。

    不到 2 秒的请求

    1. A - current_counter = 0, my_count = 0 -> 正在执行中,将在 2 秒后发送响应。
    2. B - current_counter = 0(仅在 2 秒后递增),my_count = 1 -> 陷入 while 循环条件。等待 current_counter 等于 1 以执行。
    3. C - current_counter = 0, my_count = 2 -> 像上一个请求一样卡在 while 循环中。

    2 秒后,请求 A 由 setTimeout 响应。变量 current_count 变为 1 并且请求 B 的局部变量 my_count 等于它并执行 setTimeout。因此,发送了对请求 B 的响应,并且 current_counter 在 2 秒后递增,这导致请求 C 的执行,依此类推。

    您可以将尽可能多的请求排队,但由于我自己的事件循环检查条件而仅在 2 秒后执行,而该条件又取决于仅在 2 秒后执行的 setTimeout。

    谢谢!

    【讨论】:

      猜你喜欢
      • 2018-06-04
      • 2020-10-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-21
      • 2018-03-21
      • 2019-10-15
      • 1970-01-01
      相关资源
      最近更新 更多