NodeJS 究竟如何处理高并发请求



我试图了解nodejs如何与基于线程的方法(如Servlet服务器)相比实现更高的并发性。

我已经知道在nodejs中"除了你的代码之外,一切都并行运行",并且libuv中还有一个后端线程池来处理文件IO或数据库调用,这通常是瓶颈。

所以我的问题来了:如果nodejs使用线程池来处理数据库调用,那么鉴于Tomcat也可以使用epoll/kqueue支持的NIO来实现高并发性,它如何为比Servlet服务器(如Tomcat)更高的并发请求提供服务?

例如,如果有一个 100k 个并发请求进入并且每个请求都需要数据库操作,如果要同时处理这 100k 个请求,那么使用 nodejs,我们最终仍然会创建 100k 个线程,这可能会像 Tomcat 一样导致内存耗尽。是的,100k 线程只是一个想象,因为(我知道)nodejs 有一个固定的线程池,不同的操作在事件循环中排队,但使用 Tomcat 它以相同的方式处理事情——我们也可以在 Tomcat 中配置线程池大小,它还对请求进行排队。

或者,我说"nodejs 在 libuv 中使用后端线程池来处理文件 IO 或数据库调用"是错误的吗?nodejs 是否使用 epoll/kqueue 来处理数据库 io,而无需单独的线程?

我正在阅读这个类似的问题,但仍然没有得到答案。

如果 nodejs 使用线程池来处理数据库调用

这是一个错误的假设。 NodeJS 通常使用网络与在不同进程或不同主机上运行的本地数据库进行通信。 node.js 中的网络不使用任何类型的线程 - 它使用事件驱动的 I/O。 数据库对线程的作用取决于数据库,与节点无关.js因为无论您使用哪种服务器环境,它都是相同的。

node.js 确实使用线程池进行本地磁盘访问,但大规模应用程序通常使用数据库进行磁盘访问的关键,该数据库在单独的进程中运行,并具有自己的 I/O 优化来处理大量请求。 给定数据库如何做取决于该实现,但它不会为每个请求使用 nodejs 线程。

我试图了解与基于线程的方法(如 Servlet 服务器)相比,nodejs 如何实现更高的并发性。

一般概念是,节点.js中正确编写的服务器应用程序对所有 I/O 使用异步 I/O(可能仅在服务器启动期间运行的启动代码除外)。 这意味着它可以同时使用一个 Javascript 线程同时进行大量请求,而其中大多数请求都在等待某种类型的 I/O。 如果您要同时进行大量请求,则系统以节点的方式执行此操作可能会更有效.js单线程的方式,其中所有请求都协同切换,而不是使用操作系统线程,其中每个线程都有与之关联的操作系统开销,每个抢占式线程切换都有与之关联的操作系统和 CPU 开销。

在 node-js 中,活动请求之间没有抢占式切换。 一次只能运行一个,它会一直运行到完成或命中非同步操作,并且在该异步 I/O 操作完成之前没有其他操作。 此时,JS 引擎返回到事件队列并挑选出一个事件(可能针对其他请求之一)。 这种类型的协作切换可以比操作系统级线程更快、更高效。 有时有一个编程成本,因为一个节点.js开发人员必须使用异步I/O进行编码,以便利用这一点,它具有学习曲线,以便精通编写具有适当错误处理的良好,干净的代码,并且也有用于调试它的学习曲线。

例如,如果有一个 100k 个并发请求进入并且每个请求都需要数据库操作,如果要同时处理这 100k 个请求,那么使用 nodejs,我们最终仍然会创建 100k 个线程,这可能会像 Tomcat 一样导致内存耗尽。

不,您不会创建 100k 线程。 节点.js数据库接口层,在节点.js和另一个进程或另一个主机上的实际数据库代码之间接口,可以完全在node中编写.js(使用TCP网络与数据库通信)并且根本不引入新线程,或者它可能有一些本机代码并使用少量线程进行自己的本机代码操作, 但它可能是少量的线程,甚至没有接近每个请求一个线程。

或者,我说"nodejs 在 libuv 中使用后端线程池来处理文件 IO 或数据库调用"是错误的吗? nodejs 是否使用 epoll/kqueue 来处理数据库 io,而无需单独的线程?

对于文件 I/O,是的,它在 libuv 中使用线程池。 对于数据库调用,no - 虽然详细信息完全取决于数据库实现,但通常每个数据库调用没有线程。 数据库通常在另一个进程中,数据库的 nodejs 接口库要么直接使用 nodejs TCP 与数据库通信(不使用线程),要么它有自己的本机代码插件与数据库通信,数据库可能使用少量线程进行工作,但通常不是每个请求的线程。

最新更新