什么时候Node.js阻塞?

我已经使用了一段时间的Node.js,我刚刚意识到它可以阻止。 我只是不能把我的大脑围绕Node.js阻塞的条件。

  • 所以,Node.js是单线程的,因为(i)Javascript是和(ii)避免了所有的multithreading陷阱。
  • 尽pipe是单线程的,但是要立即做很多事情,它实现了asynchronous执行。 所以,与DB(一般的I / O)交谈是非阻塞的(因为它是asynchronous的)。
  • 但是,所有传入的请求需要做一些工作(即与数据库交谈)以及必须返回给客户端的所有工作结果(即发送一些数据),他们使用该单个线程。
  • Node.js使用单线程内的“事件循环”来获取所有请求,并将其分配给非阻塞的I / O任务。

因此,由于asynchronouscallback,I / O任务是非阻塞的,但单线程可以阻塞,因为它是同步的,并且由于许多复杂的请求同时出现,事件循环可能被阻塞。

  1. 我是对的吗,我是否理解正确? 我猜,我不这样做,因为在这里和这里他们强调“节点是单线程,这意味着你的代码没有并行运行”。 这实际上是什么意思,它是如何使节点阻塞?
  2. 那么,事件循环会永远运行,并且总是search请求,或者在发现新的请求后开始执行?
  3. 节点阻塞弱点是否会使节点对大型项目无用,并最终只适用于微型站点和小型项目?

非常感谢。

首先要明确一点:node.js作为一个整体不是单线程的。 节点通过libuv有一个线程池,用于执行一些目前不可能从大多数平台上的单线程(如文件I / O)高效地执行的任务,或者它们本质上是计算密集型的(例如zlib)。 应该注意的是,大多数crypto模块(这也是固有的计算密集型)目前没有asynchronous/非阻塞接口( crypto.randomBytes()除外)。

v8还利用multithreading来完成垃圾收集,function优化等。

然而,在同一个单线程中,几乎所有其他节点都会发生。

现在专门解决您的问题:

  1. JavaScript代码从单个线程运行的事实不会造成节点阻塞。 正如这个答案所解释的,节点最重要的是(I / O)并发而不是(代码)并行。 通过在多核/ CPU系统上使用内置的cluster模块, 可以并行运行节点代码,但节点的主要目标是能够同时处理大量的I / O,而无需每个套接字/服务器的/ etc。

  2. 这里有一个很好的详细的描述,描述了节点中的事件循环是如何工作的。

  3. 如前所述,Node的主要目标是很好地处理I / O,这与Web应用程序和任何types的networking程序的大多数用例相符合。

    如果你的脚本是CPU绑定的(比如你正在计算pi或者转码audio/video),你可能最好把这个工作委派给一个子节点进程(例如调用ffmpeg进行转码,而不是用javascript或者同步在节点的主线程上的c ++节点插件)。 如果你没有在同一时间做其他事情(如处理HTTP请求),你可以在进程中做这些阻塞事情。 有很多人会以这种方式使用节点来执行各种I / O并发不重要的实用工作。 其中的一个例子可能是一个脚本,它执行js和css文件的缩小,linting和/或绑定,或者从一大组图像创build缩略图的脚本。

    但是,如果您的脚本创build了一个TCP或HTTP服务器,例如从数据库中提取信息,对其进行格式化,然后将其发回给用户,那么节点将擅长这样做,因为大部分时间都花在了进程正在等待套接字/ HTTP客户端发送(更多)数据并等待数据库回复查询结果。

让我们直接回答。

  1. 是的,如果你像这样看,Node.js是完全阻塞的。 比方说,你从数据库中读取一个巨大的,半个千兆字节的CSV文件,然后尝试对它进行编码并发送给客户端(天真,我知道,但是忍耐着我)。

JSON编码基本上是string操作。 这在很多语言中可能会很慢,而不仅仅是JavaScript。 所以如果编码这样一个json需要20秒钟,你会加载这个CSV文件(asynchronous),但是你会花20秒钟parsingstring。 在那段时间内,除了单个“JSON.stringify()”函数之外,没有别的东西可以进来 – 不是其他请求,而是其他请求,您可以同时发送到数据库/文件系统。

有一些解决这个问题的方法,但是你应该知道它 – 如果你的单个函数或像JSON.stringify这样的单个语句需要很多的话,它会被阻塞。 你需要考虑到你的应用程序。

  1. 实质上,事件循环处于hibernate状态,直到主队列上没有任务。 还有其他的队列,比如callback队列,你可以等待几十个数据库操作的结果。 但是他们不在你的主事件循环中,直到他们真的被数据库回复调回。

假设你从上面的1)parsing了这个JSON。 在此期间,您会收到5个新的请求或其他内容。 你的5个请求直接进入队列,一旦完成,事件循环检查下一个要处理的事件。 如果没有,就等待。

  1. 阻塞不会导致节点无用。 如果是这样的话,那么其他一些不像节点那样asynchronous的单线程语言会在大规模上做什么呢?

节点已经在大型项目中使用了,如果你有一点google的话,我相信你可以find很多。 诀窍是使用适当的工具来正确的解决scheme – 因此,Node.js可能需要不同的策略来处理CPU密集型任务,甚至可能不是正确的工具。

让我检查一下,如果我得到这个。

Node.js是单线程的,所以它的代码不能并行运行,但是它的I / O可以是并发的。 我们使用asynchronousJavaScript函数。 所以这就是为什么I / O是非阻塞的。

Node.js为你的代码保留一个单一的线程……但是,除了你的代码之外,所有的东西都是并行运行的。

做一个“睡眠”例如将阻塞服务器一秒钟。 – 单一代码

所有的I / O都是asynchronous的,所以下面不会阻塞服务器: c.query( 'SELECT SLEEP(20);', .... – “sleep”在一个asynchronous函数中,查询 -非阻塞I / O(从这里 )

为了pipe理传入的请求,Node实现了“事件循环”。

事件循环是“处理和处理外部事件并将其转换为callback调用的实体”。 因此,I / O调用是Node.js可以从一个请求切换到另一个请求的点。 在I / O调用中,您的代码保存callback并将控制返回到node.js运行时环境。 当数据实际可用时,稍后调用callback。 (从这里 )

所以I / O是非阻塞的,因为Node可以做别的事情,而不是等待一些I / O完成。

如果请求可能花费太长的时间来回答,那么Node将从线程池中为该请求分配一个线程。

该线程负责处理该请求,处理该请求,执行阻塞IO操作,准备响应,并依次将其发送回事件循环事件循环,并将该响应发送给相应的客户端。 (从这里 )

  1. 所以,如果有很多简单的请求,I / O是非阻塞的,这些请求的所有callback都非常快。

(从这一点来看,我不知道我是否正确)

  1. 很多简单的请求和一个复杂的。 复杂的任务可能是沉重的任务,图像resizealgorithm或任何需要时间。 每个请求都在一个asynchronous函数中,节点将为复杂的一个定义一个线程。 大多数简单的将立即作出回应。 复杂的一个需要一些时间,在自己的线程内,而简单的仍然响应(导致非阻塞)。 但事件循环中的callback按照特定的顺序排队(FIFO,对吧?)。

    在一个循环中,为下一个消息轮询队列(每个轮询被称为“tick”),当遇到消息时,执行该消息的callback。 此callback函数的调用作为调用堆栈中的初始帧,并且由于JavaScript是单线程的,所以进一步的消息轮询和处理在堆栈上的所有调用返回之前暂停。 后续(同步)函数调用将新的调用帧添加到堆栈(从这里 )

因此,复杂callback之后的简单请求的callback将需要一些时间来响应,因为复杂callback将花费大量时间。

  1. 很多复杂的请求,每个都有自己的asynchronous函数。 如果每个请求需要1秒的响应时间,并且我们有10000个响应,则时间总结。 他们最终总结了使用事件循环的单一节点。 在事件循环内部,需要花费大量时间响应的每个callback排在另一个需要花费大量时间响应的callback之后。

  2. 我认为上面描述了格兰特的问题。 这是我关于节点的缺点的第一篇文章,我仍然不知道我是否正确。 所以,

    我们的节点服务可能已经处理传入的请求,如冠军,如果它只需要返回立即可用的数据。

节点是单线程的,这意味着你的代码没有并行运行。 I / O不能阻塞服务器,但是你的代码当然可以。 如果我打电话睡5秒钟,那么我的服务器在这段时间内将不会响应。

格兰特发现自己有很多要求花费时间,因为亚马逊服务很慢

…正在等待大量的嵌套callback,这些callback都依赖于来自S3的响应(有时可能是非常慢的)

然后事件循环杀死了一切

在一个循环中,为下一个消息轮询队列(每个轮询被称为“tick”),当遇到消息时,执行该消息的callback。 此callback函数的调用作为调用堆栈中的初始帧,并且由于JavaScript是单线程的,所以进一步的消息轮询和处理在堆栈上的所有调用返回之前暂停。 后续(同步)函数调用将新的调用帧添加到堆栈…..当发生任何请求超时时,事件及其关联的callback被放在已经过载的消息队列中。 虽然超时事件可能发生在1秒,但直到队列中的所有其他消息及其相应的callback代码完成执行(可能几秒钟后),callback才得到处理。

我不知道如果我得到这个正确的。 请随时指出我的错误,并帮助我把整个事情做好。

谢谢