Node.js 和 CPU 密集型请求

Posted

技术标签:

【中文标题】Node.js 和 CPU 密集型请求【英文标题】:Node.js and CPU intensive requests 【发布时间】:2011-03-30 08:10:44 【问题描述】:

我已经开始修改 Node.js HTTP 服务器,并且非常喜欢编写服务器端 javascript,但是有些东西让我无法开始将 Node.js 用于我的 Web 应用程序。

我了解整个异步 I/O 概念,但我有点担心过程代码占用大量 CPU 资源的边缘情况,例如图像处理或对大型数据集进行排序。

据我了解,对于简单的网页请求,例如查看用户列表或查看博客文章,服务器将非常快。但是,如果我想编写非常 CPU 密集型代码(例如在管理后端)来生成图形或调整数千张图像的大小,那么请求将非常慢(几秒钟)。由于此代码不是异步的,因此在这几秒钟内到达服务器的每个请求都将被阻止,直到我的慢速请求完成为止。

一个建议是使用 Web Worker 执行 CPU 密集型任务。但是,我担心网络工作者会很难编写干净的代码,因为它通过包含一个单独的 JS 文件来工作。如果 CPU 密集型代码位于对象的方法中怎么办?为每个 CPU 密集型方法编写 JS 文件有点糟糕。

另一个建议是生成一个子进程,但这会使代码更难维护。

有什么建议可以克服这个(感知的)障碍吗?如何使用 Node.js 编写干净的面向对象代码,同时确保 CPU 繁重的任务异步执行?

【问题讨论】:

Olivier,你问了我想到的相同问题(节点新手),特别是关于处理图像的问题。在 Java 中,我可以使用固定线程 ExecutorService 并将所有调整大小作业传递给它,并等待它完成所有连接,在节点中,我还没有弄清楚如何将工作改组到限制的外部模块(让我们说)同时操作的最大数量为一次 2。你有没有找到一种优雅的方式来做到这一点? 【参考方案1】:

这是对 Web 服务器定义的误解——它应该只用于与客户端“对话”。繁重的任务应该委托给独立程序(当然也可以用 JS 编写)。 您可能会说它很脏,但我向您保证,卡在调整图像大小的 Web 服务器进程会更糟(即使是 Apache,当它不阻止其他查询时)。不过,您可以使用通用库来避免代码冗余。

编辑:我想出了一个类比; Web 应用程序应该像餐厅一样。你有服务员(网络服务器)和厨师(工人)。服务员与客户联系并做一些简单的任务,例如提供菜单或解释某道菜是否是素食。另一方面,他们将更艰巨的任务委托给厨房。因为服务员只做简单的事情,他们反应迅速,厨师可以专注于他们的工作。

这里的 Node.js 将是一个单一但非常有才华的服务员,可以一次处理许多请求,而 Apache 将是一群愚蠢的服务员,每个人只处理一个请求。如果这个 Node.js 服务员开始做饭,那将是一场直接的灾难。尽管如此,即使是大量的 Apache 服务员,做饭也可能会耗尽,更不用说厨房里的混乱和反应能力的逐渐下降了。

【讨论】:

嗯,在 Web 服务器是多线程或多进程并且可以处理多个并发请求的环境中,在单个请求上花费几秒钟是很常见的。人们已经开始期待这一点。我想说的误解是 node.js 是一个“常规”网络服务器。使用 node.js 你必须稍微调整你的编程模型,这包括将“长时间运行”的工作推给一些异步工作人员。 不要为每个请求生成一个子进程(这违背了 node.js 的目的)。仅从您的繁重请求中产生工人。或者将繁重的后台工作转移到 node.js 以外的其他地方。 很好的比喻。我有孩子,这让我想起了电影《料理鼠王》中的高潮场景,那个家伙是孤独的服务员,穿着溜冰鞋到处跑,厨房里有一群小工人(老鼠)。 :D 哈,我真的很喜欢。 “Node.js:让不良做法效果不佳” @mbq 我喜欢这个类比,但它可能需要一些工作。传统的多线程模型将是一个既是服务员又是厨师的人。一旦订单被接受,该人必须回去做饭,然后才能处理另一个订单。 node.js 模型将节点作为服务员,将网络工作者作为厨师。服务员处理获取/解析请求,而工作人员管理更耗时的任务。如果您需要扩大规模,您只需将主服务器设置为节点集群,并将 CPU 密集型任务反向代理到为多线程处理而构建的其他服务器。【参考方案2】:

你需要的是一个任务队列!将您长期运行的任务移出网络服务器是一件好事。将每个任务保存在“单独的”js 文件中可以促进模块化和代码重用。它迫使您考虑如何以一种从长远来看更容易调试和维护的方式来构建您的程序。任务队列的另一个好处是工人可以用不同的语言编写。只需弹出一个任务,完成工作,然后写回响应。

类似https://github.com/resque/resque

这里有一篇来自 github 的文章,关于他们为什么要构建它 http://github.com/blog/542-introducing-resque

【讨论】:

为什么要在一个专门针对节点世界的问题中链接到 Ruby 库? @JonathanDumaine 这是一个很好的任务队列实现。 Rad ruby​​ 代码并用 javascript 重写它。利润! 我是 gearman 的忠实拥护者,gearman 工作人员不会轮询 Gearman 服务器以获取新工作 - 新工作会立即推送给工作人员。反应灵敏 其实已经有人移植到node世界了:github.com/technoweenie/coffee-resque @pacerier,你为什么这么说?你有什么建议?【参考方案3】:

您不希望 CPU 密集型代码异步执行,而是希望它并行执行。您需要从服务 HTTP 请求的线程中获取处理工作。这是解决这个问题的唯一方法。使用 NodeJS,答案是 cluster module,用于生成子进程来完成繁重的工作。 (AFAIK Node 没有任何线程/共享内存的概念;它是进程或什么都不是)。对于如何构建应用程序,您有两种选择。您可以通过生成 8 个 HTTP 服务器并在子进程上同步处理计算密集型任务来获得 80/20 解决方案。这样做相当简单。您可能需要一个小时在该链接上阅读它。实际上,如果您只是撕掉该链接顶部的示例代码,您将获得 95% 的成功。

另一种构建它的方法是设置一个作业队列并通过队列发送大型计算任务。请注意,作业队列的 IPC 有很多开销,因此这仅在任务明显大于开销时才有用。

我很惊讶这些其他答案都没有提及集群。

背景: 异步代码是暂停直到发生某事其他地方的代码,此时代码唤醒并继续执行。一种很常见的情况是 I/O 必须在其他地方发生一些缓慢的事情。

如果是你的处理器负责处理工作,那么异步代码就没用了。这正是“计算密集型”任务的情况。

现在,异步代码似乎是小众,但实际上它非常普遍。它恰好对计算密集型任务没有用处。

例如,等待 I/O 是一种经常发生在 Web 服务器中的模式。每个连接到您的服务器的客户端都会获得一个套接字。大多数时候套接字是空的。在套接字接收到一些数据之前,您不想做任何事情,此时您想要处理请求。在底层,像 Node 这样的 HTTP 服务器正在使用事件库 (libev) 来跟踪数千个打开的套接字。操作系统会通知 libev,然后当其中一个套接字获取数据时,libev 会通知 NodeJS,然后 NodeJS 会在事件队列中放置一个事件,此时您的 http 代码会启动并一个接一个地处理这些事件。在套接字有一些数据之前,事件不会被放入队列,因此事件永远不会等待数据 - 它已经为它们准备好了。

单线程基于事件的 Web 服务器作为一种范式是有意义的,因为瓶颈正在等待一堆大部分为空的套接字连接,并且您不希望每个空闲连接都有一个完整的线程或进程,并且您不想轮询您的 250k 套接字以查找下一个包含数据的套接字。

【讨论】:

应该是正确的答案....至于生成 8 个集群的解决方案,您需要 8 个核心对吗?或具有多台服务器的负载平衡器。 还有什么是了解第二种解决方案的好方法,即设置队列。队列的概念很简单,但它是进程和队列之间的消息传递部分,这是外来的。 没错。您需要以某种方式将工作转移到另一个核心上。为此,您需要另一个核心。 回复:队列。实际的答案是使用作业队列。有一些可用于节点。我从来没有使用过其中任何一个,所以我无法提出建议。奇怪的答案是工作进程和队列进程最终将通过套接字进行通信。 '如果是你的处理器负责处理工作,异步代码就没用了。这正是“计算密集型”任务的情况。这总结了一切!【参考方案4】:

您可以使用的几种方法。

正如@Tim 所说,您可以创建一个异步任务,该任务位于您的主要服务逻辑之外或与之平行。取决于您的确切要求,但即使cron 也可以充当排队机制。

WebWorkers 可以为您的异步进程工作,但 node.js 目前不支持它们。有几个扩展提供支持,例如:http://github.com/cramforce/node-worker

您仍然可以通过标准的“需要”机制重用模块和代码。您只需要确保对工作人员的初始分派传递了处理结果所需的所有信息。

【讨论】:

【参考方案5】:

使用child_process 是一种解决方案。但是与 Go 相比,每个生成的子进程可能会消耗大量内存 goroutines

您也可以使用基于队列的解决方案,例如kue

【讨论】:

以上是关于Node.js 和 CPU 密集型请求的主要内容,如果未能解决你的问题,请参考以下文章

Node.js 和 CPU 密集型请求

Node.js - 真正的大流是阻塞和 CPU 密集型的

Node.js 性能优化

使用 Express/Node.js 和 Angular 处理取消的请求

服务器可以检查请求是不是来自 iframe (Node.js)?

node.js适合做啥网站