具有多个并发请求的Node.js服务器,它如何工作?
我知道node.js是单线程,异步,无阻塞的I / O。我已经读了很多。例如,PHP每个请求使用一个线程,但是节点仅对所有线程使用一个线程。
假设有三个请求a,b,c同时到达node.js服务器。这些请求中的三个需要大型阻止操作,例如,它们都希望读取相同的大文件。
然后,如何将请求排队,将按什么顺序执行阻塞操作,以及按什么顺序分派响应?当然使用多少个线程?
请告诉我三个请求从请求到响应的顺序。
回答:
- 三个请求被发送到node.js Web服务器。
- 无论哪个请求在其他两个请求之前到达,都会触发Web服务器请求处理程序并开始执行。
- 其他两个请求进入node.js事件队列,等待轮到他们。从技术上讲,是否将等待请求在传入的TCP级别上排队还是在node.js内部排队(实际上我不知道)取决于node.js实现的内部,但是出于讨论的目的,所有重要的是,传入事件已排队并且在第一个请求停止运行之前不会触发。
- 该第一个请求处理程序将一直执行到遇到异步操作(例如读取文件)为止,然后再无其他操作,直到异步操作完成为止。
- 此时,异步文件I / O操作将启动,并且原始请求处理程序将返回(此操作已完成)。
- 由于第一个请求(正在等待文件I / O)现在已经返回,因此node.js引擎现在可以将下一个事件从事件队列中拉出并启动它。这是到达服务器的第二个请求。它在第一个请求时将经历相同的过程,并将一直运行直到无其他事情为止(并且还在等待文件I / O)。
- 当第二个请求返回系统时(因为它正在等待文件I / O),则第三个请求可以开始运行。它将遵循与前两个相同的路径。
- 现在,当第三个请求也正在等待I / O并返回系统时,node.js可以自由地将下一个事件从事件队列中拉出。
- 此时,所有三个请求处理程序都同时处于“运行中”状态。实际上一次只能运行一次,但是所有一次都可以运行。
- 事件队列中的下一个事件可能是某个其他事件或某个其他请求,也可能是前三个文件I / O操作之一的完成。队列中下一个事件将开始执行。假设它是第一个请求的文件I / O操作。此时,它将调用与该第一个请求的文件I / O操作相关联的完成回调,并且该第一个请求开始处理文件I / O结果。然后,此代码将继续运行,直到完成整个请求并返回,或者开始其他异步操作(如更多文件I / O)并返回为止。
- 最终,第二个请求的文件I / O将准备就绪,该事件将从事件队列中拉出。
- 然后,对第三个请求相同,最终所有三个请求都将完成。
因此,即使实际上实际上只有一个请求同时在执行,多个请求也可以同时处于“处理中”或“运行中”状态。有时这称为协作多任务,而不是通过具有多个本机线程的“抢先式”多任务,系统可以随时在这些线程之间自由切换,给定的Javascript线程运行直到返回系统,然后再返回,只有这样,才能开始运行另一段Javascript。因为一段Javascript可以启动非阻塞异步操作,所以Javascript线程可以在其异步操作仍处于挂起状态时返回到系统(使其他Javascript片段可以运行)。这些操作完成后,
这里的关键点是,给定的Javascript线程将一直运行,直到返回系统为止。如果在执行过程中启动了一些异步操作(例如文件I /
O或网络连接),那么当这些事件结束时,它们会将一个事件放入事件队列中,并且当JS引擎完成运行任何事件之前,它,该事件将得到处理,并将导致调用回调,并且该回调将轮流执行。
与多线程模型相比,这种单线程性质极大地简化了并发的处理方式。在每个请求都启动其自己的线程的完全多线程环境中,所有希望共享的数据,甚至是一个简单的变量,都必须遵守竞争条件,并且必须使用互斥体进行保护,然后任何人才能读取它。
在Javascript中,因为没有多个请求的并发执行,所以对于简单的共享变量访问不需要互斥体。从定义上看,一段Javascript正在读取一个变量,此时没有其他Javascript在运行(单线程)。
注意的一种技术区别是,只有Javascript的执行是单线程的。node.js内部构件确实将线程本身用于某些方面。例如,异步文件I /
O实际上使用本机线程。网络I / O实际上并不使用线程(它使用本机事件驱动的网络)。
但是,在node.js内部使用线程不会直接影响Javascript执行。一次仅执行一个Java线程。
启动异步操作时,在修改状态的过程中仍可能存在争用条件,但是这种方法比多线程环境中的方法少见,而且更容易识别和保护这些情况。作为可能存在的竞争条件的示例,我有一个简单的服务器,该服务器使用间隔计时器每10秒从多个温度探测器读取一次读数。它从所有这些温度读数中收集数据,并且每小时将其写出到磁盘。它使用异步I
/ O将数据写入磁盘。但是,由于使用了许多不同的异步文件I / O操作将数据写入磁盘,因此间隔计时器可能会在某些异步文件I /
O操作之间触发,从而导致服务器所在的数据写入磁盘的中间要修改。这很糟糕,可能导致写入不一致的数据。在一个简单的世界中,可以通过在开始将所有数据复制到磁盘之前对其进行复制来避免这种情况,因此,如果在将数据写入磁盘时出现新的温度读数,则该复制将不会受到影响,并且代码仍将一组一致的数据写入磁盘。但是,对于此服务器,数据可能很大,而服务器上的内存却很小(这是Raspberry
Pi服务器),因此在内存中复制所有数据是不切实际的。副本将不受影响,并且代码仍将一致的数据集写入磁盘。但是,对于此服务器,数据可能很大,而服务器上的内存却很小(这是Raspberry
Pi服务器),因此在内存中复制所有数据是不切实际的。副本将不受影响,并且代码仍将一致的数据集写入磁盘。但是,对于此服务器,数据可能很大,而服务器上的内存却很小(这是Raspberry
Pi服务器),因此在内存中复制所有数据是不切实际的。
因此,可以通过在将数据写入磁盘的过程中设置一个标志,然后在将数据写入磁盘后清除该标志来解决该问题。如果在设置此标志的同时触发间隔计时器,则会将新数据放入单独的队列中,并且不会修改正在写入磁盘的核心数据。将数据写入磁盘后,它将检查队列并将发现的所有温度数据添加到内存中的温度数据中。将保留正在写入磁盘的过程的完整性。每当此“竞赛条件”被击中并且由于该原因数据进入队列时,我的服务器都会记录一个事件。而且,瞧,它确实每隔一段时间就会发生一次,并且保存数据完整性的代码也起作用了。
以上是 具有多个并发请求的Node.js服务器,它如何工作? 的全部内容, 来源链接: utcz.com/qa/426138.html