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

时间:2017-07-16 07:55:22

标签: node.js multithreading concurrency

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

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

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

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

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

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

1 个答案:

答案 0 :(得分:1)

  

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

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

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

  

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

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

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

  

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

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

  

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

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