事件驱动与非事件驱动 Web 服务器中的线程

12
以下两个图表展示了在事件驱动的Web服务器(如Node.js + JavaScript)和传统非事件驱动的Web服务器(如IIS + C#)中线程如何工作的理解。
传统(非事件驱动)Web服务器: 传统(非事件驱动)Web服务器 事件驱动的Web服务器: 事件驱动的Web服务器 从图表可以很容易地看出,在传统Web服务器上使用线程执行3个长时间运行的操作的数量大于在事件驱动Web服务器上的线程数量(3 vs 1)。
以下是我的问题: 1. 是否正确假设事件驱动的场景只使用了一个线程?这显然不正确,一定有些东西被创建来处理I/O任务。对吗? 2. 事件驱动的服务器如何处理I / O?比如说I / O是从数据库读取数据。我认为Web服务器必须创建一个线程来处理连接到数据库的工作。对吗? 3. 如果事件驱动Web服务器确实创建了线程来处理I / O,那么它的益处在哪里? 4. 我的困惑可能的一个解释是,在传统和事件驱动的两种情况下,确实创建了三个单独的线程来处理I / O(未在图片中显示),但区别真正在于Web服务器本身的线程数,而不是I/O线程。这是否准确?

我查了一下“event-driven web server”,但这个词组并不太准确,因为所有的Web服务器都会响应事件。更好的描述应该是“异步Web服务器”。 - Robert Harvey
在Node.js(即事件驱动的服务器)中,用户代码运行在单个线程中,但Node服务器本身(框架)使用多个线程来处理I/O。 - Aaron
@Aaron 如果Node使用线程来处理I/O,那么这是有道理的,但如果是这样,人们为什么会说Node.js使用比传统方法更少的线程呢?难道问题#4是正确的吗? - Hector Correa
@RobertHarvey 我认为这是合理的。事件驱动和事件可用之间有区别。在Node中,几乎所有东西都建立在事件回调上。在我每天看到的C#和Java中,它更像是非常小的类意大利面条,偶尔会有观察者肉丸子。 - Erik Reppen
1
  1. Node 从线程池中获取线程。
  2. 当数据准备好时,创建的线程回调到主线程。
  3. 现在它是按请求生成的,它使用有限大小的池,因此您不必支付太多的上下文切换费用。
  4. 在传统服务器中,在一个线程中执行 IO 是可以的,因为它是单独的线程,除非显式地没有更多的线程被生成,否则 IO 和应用程序都在该线程中运行。
- Yury Solovyov
2个回答

6
  1. Node使用线程来处理IO操作。JavaScript代码运行在单个线程中,但所有的IO请求都在并行线程中运行。如果想要让一些JS代码在并行线程中运行,则可以使用thread-a-gogo或其他可用的包来缓解这种行为。

  2. 1. 一样,Node会为IO操作创建线程。

  3. 除非你想要处理线程,否则不需要处理线程。这样更易于开发。至少这是我的观点。

  4. 一个Node应用程序可以被编写成像另一个Web服务器一样运行。通常,JS代码运行在单个线程中,但有方法可以使其表现不同。

个人而言,如果想要尝试使用线程,我建议使用threads-a-gogo(虽然包名不太具有启示性,但很容易使用)。它更快。 Node还支持多个进程,如果也想尝试那种方式,可以运行一个完全独立的进程。


谢谢。我并不是在寻找如何在Node.js中使用线程的方法。我只是试图理解当人们说Node.js使用比传统Web服务器更少的线程时,它的优势在哪里。如果I/O在单独的线程上处理(这是有道理的),那么在图片中的情况下,两个实例都将使用3个线程,对吗?或者在传统模式下会使用6个线程,在事件驱动模式下会使用3个线程? - Hector Correa
1
Node.js 不会创建线程。libuv 可能会使用内部线程池,但这取决于环境,它在 Windows 上使用 IOCP。 - Raynos
1
@Raynos 但是如果维基百科没有骗我,IOCP本身就是在线程管理之上的一层。 - Erik Reppen
1
区别更多地在于思想,像Apache这样的服务器会生成一个线程并运行整个应用程序直到完成。Node仅针对IO使用线程,仅在资源不支持异步处理时使用,例如某些数据库、磁盘IO和其他一些内容。应用程序本身在主线程中运行 - 每个进程一个。 - Yury Solovyov
4
要真正理解区别,看看随着服务器规模的扩大,线程数的数量。对于类似Apache的服务器,每个连接有1个线程,因此对于繁忙的服务器,你可能会拥有数百甚至数千个线程。对于Node.js,线程池的大小是固定的。无论服务器有多忙碌,线程计数将保持在10个左右。 - andy

1
NodeJS最好的比喻是像一只疯狂的松鼠(即您的线程),在一个无限数量的鸽子(您的I/O)可用于传递消息的轮子上奔跑。
在Node中,I/O是“免费”的。您的松鼠工作是设置连接并发送鸽子,然后可以继续做其他事情,而鸽子检索数据时,只有在鸽子返回时才处理数据。
如果编写糟糕的代码,您可能会让松鼠等待每只鸽子。
因此,始终编写非阻塞I/O代码。
如果您可以鼓励您的鸽子承诺回来;)
Promise和生成器可能是您可以采取的最佳方法。
但是,您始终可以使用Node集群来建立一个主松鼠,该松鼠将基于主松鼠找到的CPU数量繁殖子松鼠以完成工作分配。
希望这可以帮助您,并注意完全没有汽车类比。

1
同时,在 PETA 的另一边 - gunr2171
在Node中进行IO操作并不是免费的,即使不是“免费”的,它仍然有成本,并且您仍然可能耗尽内部线程池中的线程。 - Yury Solovyov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接