2017-07-16 143 views
1

我想了解与基于线程的方法(如Servlet服务器)相比,nodejs如何实现更高的并发性。NodeJS如何处理高并发请求?

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

所以,这里是我的问题:如果nodejs使用线程池来处理数据库调用,那么它如何能够服务更高的并发请求,比Tomcat等Servlet服务器更好,因为Tomcat也可以使用由epoll/kqueue支持的NIO来实现高并发性?例如,如果有100k个并发请求进入并且每个请求都需要数据库操作,如果这些100k请求需要同时服务,那么使用nodejs,我们最终还是会创建100k个线程,这可能会导致内存耗尽,正如Tomcat所做的那样。是的,100k线程只是一个想象,因为(我知道)nodejs有一个固定的线程池,不同的操作在事件循环中排队,但是使用Tomcat它以相同的方式处理事情 - 我们也可以配置线程池在Tomcat中的大小,它也排队请求。

或者,我错了,说“nodejs使用libuv中的后端线程池来处理文件IO或数据库调用”? nodejs是否使用epoll/kqueue来处理数据库io而没有单独的线程?

我在读this similar question,但仍然没有得到答案。

回答

0

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

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

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

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

一般概念是node.js中正确编写的服务器应用程序对所有I/O使用异步I/O(可能启动代码只在服务器启动时运行)。这意味着它可以在同一时间只有一个单独的Javascript线程的同时有很多请求正在运行,而大多数请求正在等待某种类型的I/O。如果您将同时有很多请求正在进行中,那么系统可以更有效地执行单个线程的node.js方法,其中所有请求都协作切换到使用操作系统线程,其中每个线程都有与之相关的操作系统开销,并且每个抢占线程交换机都有与之相关的操作系统和CPU开销。

在node-js中,活动请求之间没有先发制人的切换。一次只能运行一个,它会一直运行,直到完成或命中异步操作,并且在异步I/O操作完成之前没有别的事情要做。此时,JS引擎返回到事件队列并挑选一个事件(可能用于其他请求之一)。这种类型的协作交换可以比OS级别的线程更快且更高效。有时候编程成本是因为node.js开发人员必须使用异步I/O编写代码才能利用这一点,它具有学习曲线,以便熟练编写具有适当错误处理的良好,干净的代码,并具有一个用于调试的学习曲线。

例如,如果有即将在10万并发请求和每个需要的数据库操作,如果这10万的请求应同时提供服务,用的NodeJS我们还是最终创造10万线为Tomcat的确实这可能会导致内存耗尽。

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

或者,我错了,说“nodejs在libuv中使用后端线程池来处理文件IO或数据库调用”? nodejs是否使用epoll/kqueue来处理数据库io而没有单独的线程?

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

+0

感谢您的全面解释。当我谈到数据库调用时,主要是指来自客户端的调用,在这种情况下,我们的js代码充当数据库客户端。是的,数据库调用最终是网络套接字io,所以我可以说,在数据库调用来自客户端的数据库调用覆盖的底层io机制,如epoll/kqueue包装libuv?否则我无法想象在数据库调用异步时没有使用线程的场景。 – davenkin

+0

@davenkin - 您的数据库评论令我困惑。通常,运行数据库的实际代码将与node.js(有时在另一个主机上)处于不同的进程中。当node.js中的Javascript向客户端调用数据库时,它将准备与其他进程或其他主机的某些通信。该通信通常不会使用node.js线程,因为它通常是非阻塞TCP并且node.js不会为每个TCP连接使用额外的线程。 – jfriend00

+0

@davenkin - 给你一个想法。如果我编写node.js应用程序来向100个不同的主机发起http API调用,以便所有100个请求同时处于运行状态并等待响应,但不会在node.js中使用任何其他线程。现在,如果node.js中的数据库驱动程序具有自己的本机代码,那么它可以在其自己的本机代码中执行任何它想要的操作。有些人可能会使用一些本地线程来跟踪请求 - 我不知道。但是,根本不需要node.js模型。 – jfriend00