让我检查一下。
Node.js是单线程的,因此它的代码不能并行运行,但其I/O可以是并发的。我们使用异步JavaScript函数来实现这一点。所以这就是为什么I/O是非阻塞的。
Node.js为您的代码保留了一个单线程......但是,除了您的代码之外,所有内容都在并行运行。
例如,进行“休眠”将会使服务器阻塞一秒钟。- 单线程代码
所有I/O都是事件驱动和异步的,因此以下操作不会阻塞服务器:c.query('SELECT SLEEP(20);', ....
- “休眠”位于异步函数内,查询 - 非阻塞I/O(来源:here)
为了管理传入的请求,Node实现了“事件循环”。
事件循环是“处理和处理外部事件并将它们转换为回调调用的实体”。因此,I/O调用是Node.js可以从一个请求切换到另一个请求的点。在I/O调用中,您的代码保存回调并将控制返回给node.js运行时环境。当数据实际可用时,稍后将调用回调。
(来自
这里)
因此,I/O是非阻塞的,因为Node可以做其他事情而不是等待某些I/O完成。
如果请求需要太长时间才能回答,Node将为该请求分配一个线程池中的线程。
那个线程负责接收该请求,处理它,执行阻塞IO操作,准备响应并将其发送回事件循环。事件循环再将响应发送给相应的客户端。(来自
这里)
- 因此,如果有很多简单的请求,I/O 是非阻塞的,并且这些请求的所有回调都能够非常快速地响应。
(从这一点开始我不确定我是否理解正确)
2. 许多简单的请求和一个复杂的请求。 复杂的请求可能是一个繁重的任务、一个图像调整算法或任何需要时间的东西。 每个请求都在一个异步函数中,Node将为复杂的请求定义一个线程。 大多数简单的请求将立即响应。 复杂的请求将花费一些时间,在它自己的线程中,而简单的请求仍然在响应(因为非阻塞)。 但是事件循环中的回调按特定顺序排队(先进先出,对吗?)。
“在一个循环中,队列会轮询下一条消息(每次轮询称为“tick”),当遇到消息时,将执行该消息的回调函数。调用此回调函数作为调用堆栈中的初始帧,由于JavaScript是单线程的,因此在堆栈上所有调用返回之前,进一步的消息轮询和处理将停止。后续(同步)函数调用将向堆栈添加新的调用帧。”(来自
这里的引用)
所以在复杂请求的回调之后进行的简单请求的回调需要一些时间来响应,因为复杂请求的回调需要很长时间。
3. 大量复杂请求,每个请求都在其自己的异步函数内部。如果每个请求需要1秒钟才能响应,而我们有10000个响应,则时间总和会增加。它们最终都会在使用事件循环的单线程节点中累加。在事件循环内部,每个需要很长时间响应的回调函数都排在另一个需要很长时间响应的回调函数后面。
我认为以上描述了Grant的问题
here。那是我第一次读到有关node缺点的文章,至今仍不知道是否理解正确。因此,
我们的Node服务可能已经像冠军一样处理了传入请求,如果它所需的只是立即可用的数据。
但是,
Node是单线程的,这意味着你的代码不会并行运行。I/O可能不会阻塞服务器,但你的代码肯定会。如果我调用sleep 5秒钟,我的服务器在此期间将无响应。
Grant发现自己有很多请求需要花费时间,因为亚马逊服务很慢。
......正在等待大量嵌套回调,所有这些回调都依赖于来自S3的响应(有时可能非常慢)。
然后事件循环杀死了一切。
在循环中,队列轮询下一个消息(每个轮询称为“tick”),当遇到消息时,执行该消息的回调。调用此回调函数作为调用堆栈中的初始帧,并且由于JavaScript是单线程的,进一步的消息轮询和处理在返回堆栈上所有调用之前停止。随后(同步)函数调用向堆栈添加新的调用帧.....当任何请求超时发生时,事件及其关联的回调被放置在已经过载的消息队列上。虽然超时事件可能发生在1秒钟内,但在所有其他消息当前在队列上以及它们相应的回调代码完成执行之前,回调不会得到处理(可能几秒钟后)。
我不确定我是否理解正确。请随意指出我的错误并帮助我完全正确。谢谢。