当前位置: 首页 > 后端技术 > Node.js

NodeJS架构-单线程事件循环模型

时间:2023-04-03 20:57:12 Node.js

本翻译章节探讨了NodeJS的架构和单线程事件循环模型。在这篇文章中,我们将讨论“NodeJS是如何工作的,它遵循什么类型的处理模型,NodeJS如何使用单线程模型处理并发请求”等等。NodeJS单线程事件循环模型正如我们刚才所说,NodeJS使用“单线程事件循环模型”架构来处理多个并发的客户端请求。Web应用技术有很多,如JSP、SpringMVC、ASP.NET等。但所有这些技术都遵循“多线程请求-响应”架构来处理多个并发客户端。我们已经熟悉“多线程请求-响应”架构,因为它被大多数Web应用程序框架使用。但是为什么NodeJS选择了不同的架构来开发Web应用程序。多线程和单线程事件循环架构之间的主要区别是什么?NodeJSNodeJS使用“单线程事件循环模型”架构来处理多个并发客户端。然而,它如何在不使用多线程的情况下真正处理并发客户端请求。什么是事件循环模型?我们将一一讨论这些概念。在讨论“单线程事件循环”架构之前,我们先来介绍下著名的“多线程请求-响应”架构。传统的Web应用程序处理模型任何非NodeJS开发的Web应用程序通常都遵循“多线程请求-响应”模型。我们可以将此模型称为请求/响应模型。客户端向服务器发送请求,然后服务器根据客户端请求进行一些处理,准备响应并将其发送回客户端。该模型使用HTTP协议。由于HTTP是一种无状态协议,因此这种请求/响应模型也是一种无状态模型。所以我们可以称之为请求/响应无状态模型。但是,此模型使用多线程处理并发客户端请求。在讨论该模型的内部结构之前,请务必先了解以下内容。请求/响应模型处理的步骤:客户端向web服务器发送请求web服务器内部维护一个有限的线程池来服务客户端的请求web服务器处于无限循环中,等待客户端传入的请求Web服务器处理请求步骤:接收客户端请求从线程池中选择一个线程将此线程分配给客户端请求该线程读取客户端请求,处理客户端请求,执行阻塞IO操作(如果需要)并准备响应到这个线程将准备好的请求发回给web服务器web服务器将这个响应发送给相应的服务器服务器为所有客户端执行上述步骤,为每个客户端请求创建一个线程。图解说明:Client-1、Client-2、...、Client-n是同时向web服务器发送请求的客户端应用。Web服务器在内部维护一个有限的线程池。线程池中的线程数为m个web服务器一个一个接收这些请求:web服务器拿起Client-1的请求Request-1,从线程池中拿起一个线程T-1并将这个请求分配给线程T-1线程T-1读取Client-1的请求Request-11,并处理该请求。这个请求是非阻塞IO处理。处理完必要的步骤后,准备将Response-1发送回客户端Web服务器,并将这个Response-1发送给Client-1。Web服务器拿起Client-2的请求Request-2,从线程池中拿起一个线程T-2并将这个请求分配给线程T-2。线程T-2读取Client-2的请求Request-2,并处理该请求。这个请求的非阻塞IO处理已经完成了必要的步骤之后准备将Response-2发送回客户端Web服务器并将这个Response-2发送给Client-2Web服务器拿起Client-n的请求Request-n,从线程池中取出一个线程T-n,将这个请求分配给线程T-n线程T-n读取Client-n的请求Request-n,处理请求Request-n需要大量的阻塞IO和计算操作线程T-n需要更多的时间来与外部系统(SQL,文件系统)交互,执行必要的步骤并准备Response-n并将其发送回服务器Web服务器将此Response-n发送给Client-n如果'n'大于'm'(大多数时间,是真的),那么在使用完所有m个线程后,剩下的客户端请求会在队列中等待。如果这些线程中有大量的阻塞IO操作(例如:与数据库、文件系统、外部服务等交互),那么剩下的客户端也会等待更长时间。一旦线程池中的线程空闲并可用于下一个任务,服务器就会选择这些线程并将它们分配给剩余的客户端请求。每个线程都会使用很多资源,比如内存等等。因此,在将这些线程从忙碌状态转为等待状态之前,应该释放所有获取的资源。请求/响应无状态模型的缺点:当处理越来越多的并发客户端请求时,它会变得棘手当客户端请求增加时,线程也会增加,最终占用更多内存。客户端可能需要等待服务器释放可用线程来处理他们的请求处理阻塞IO任务时浪费时间NodeJS架构-单线程事件循环NodeJS不遵循请求/响应多线程无状态模型。它使用单线程和事件循环模型。NodeJS的处理模型主要是基于Javascript事件模型和Javascript回调机制。由于NodeJS遵循的架构,它可以非常轻松地处理越来越多的并发客户端请求。在讨论这个模型的内部结构之前,先看看下图。我试图设计这张图来解释NodeJS内部的每一点。NodeJS处理模型的主要核心是“事件循环(EventLoop)”。如果我们理解了这一点,那么就很容易理解NodeJS的内部架构。单线程事件循环模型的处理步骤客户端向Web服务器发送请求NodeJSWeb服务器内部维护了一个有限的线程池来服务客户端请求NodeJSWeb服务器接收这些请求并将它们放入队列中。它被称为“事件队列”,NodeJS的Web服务器内部有一个称为“事件循环”的组件,它使用无限循环来接收请求并处理它们。事件循环只使用一个线程,这是NodeJS处理模型的核心事件循环,用于检查是否有客户端请求放入事件队列。如果没有,它将等待事件队列中的请求。如果存在,则从事件队列中获取客户端请求:如果客户端请求不需要任何阻塞IO操作,则开始处理客户端请求,处理所有内容,准备响应,如果客户端请求,则将其发送回客户端如果请求需要一些阻塞IO操作,比如与数据库、文件系统、外部服务交互,那么它会遵循不同的方法:从内部线程池中获取一个线程检查线程可用性,并将这个客户端请求分配给这个线程这个线程负责接收请求,处理请求,执行阻塞IO操作,准备响应并发送回事件循环事件循环依次将响应发送给对应的客户端图解:Client-1,Client-2,...,Client-n是同时向Web服务器发送请求的客户端应用程序。Web服务器维护一个有限的线程池。线程池中的线程数为m。NodeJSWeb服务器收到Client-1,Client-2,...,Client-n的请求后,将请求放入事件队列NodeJS的事件循环从队列中拾取这些请求:事件循环拾取Client-1的请求Request-1检查Client-1Request-1是否真的需要任何阻塞IO操作,或者需要更多时间来执行复杂的计算任务。由于这个请求是一个简单的计算和非阻塞IO任务,所以不需要单独的线程来处理。Loop发送Response-1给Client-1Eventloop拾取Client-2的请求Request-2检查Client-2是否需要任何阻塞的IO操作或者需要更多的时间来执行复杂的计算任务因为这个请求是简单的计算而不是-阻塞IO任务,因此不需要单独的线程来处理事件循环所需的操作来处理请求,准备其对Response-2事件循环的响应,将Response-2发送到Client-2事件循环,然后接Client-n的requestRequest-n检查Client-nRequest-n是否需要任何阻塞IO操作或需要更多时间来执行复杂的计算任务由于这个请求有非常复杂的计算或阻塞IO任务,事件循环不会从内部线程池中获取线程T-1来处理这个请求Event循环,并将这个Client-nRequest-n分配给线程T-1。线程T-1读取并处理Request-n,进行必要的阻塞IO或计算任务,最后准备响应Response-n线程T-1将这个Response-n发送到事件循环eventloopdepends这次将这个Response-n发送给Client-n,这里的客户端请求是对一个或多个JavaScript函数的调用,因为JavaScript函数可以调用其他函数或者可以利用它们的回调函数属性,所以每个客户端的请求处理都是它看起来像这样:例如:function1(function2,callback1);函数2(函数3,回调2);function3(输入参数);NodeJS的单线程事件循环的优点处理越来越多并发的客户端请求是非常容易的,因为事件循环的存在,即使我们的NodeJS应用收到越来越多的并发请求,我们也不需要创建很多线程。NodeJS使用的线程较少,因此对资源和内存的使用也较少。原文地址:NodeJS架构——单线程事件循环