logo

我之前学习 Node 的犹如蜻蜓点水一般,现在都谈不上对 Node 有了解,所以又重新想了一下这个问题,没有找到一些比较满意的答案,不过有些收获的这里总结一下。

为什么使用 Node.js

原文 Why The Hell Would I Use Node.js? A Case-by-Case Tutorial
作者 TOMISLAV CAPAN

正如维基百科中说明的一样

Node.js 是能够在服务器端运行 JavaScript 的开放源代码、跨平台运行环境。Node.js 采用 Google 开发的 V8 运行代码,使用事件驱动、非阻塞和异步输入输出模型等技术来提高性能,可优化应用程序的传输量和规模。

在本 Node.js 指南中,我不仅会讨论这些优势是如何实现的,还会讨论为什么您可能想要使用 Node.js 以及为什么不使用一些经典的 Web 应用程序模型作为示例。

它是如何工作的

Node.js 的主要思想:使用非阻塞、事件驱动的 I/O,在面对跨分布式设备运行的数据密集型实时应用程序时保持轻量级和高效。

它的真正含义是,Node.js 并不是一个将主导 Web 开发世界的银弹新平台。相反,它是一个满足特定需求的平台。

理解这一点是绝对必要的。您绝对不想将 Node.js 用于 CPU 密集型操作;事实上,将它用于繁重的计算几乎会抵消它的所有优势。Node 真正闪耀的地方在于构建快速、可扩展的网络应用程序,因为它能够以高吞吐量处理大量同时连接,这相当于高可扩展性

它在后台的工作方式非常有趣。与传统的 Web 服务技术相比,每个连接(请求)产生一个新线程,占用系统 RAM 并最终最大化可用 RAM 量,Node.js 在单线程上运行,使用非阻塞 I/ O 调用,允许它支持在事件循环中保持的数万个并发连接。

image

快速计算:假设每个线程可能附带 2 MB 内存,在具有 8 GB RAM 的系统上运行使我们理论上最多可以有 4,000 个并发连接(计算取自 Michael Abernethy 的文章“到底什么是 Node .js?”,于 2011 年在 IBM developerWorks 上发表;不幸的是,该文章不再可用),加上线程之间上下文切换的成本。这就是您通常在传统 Web 服务技术中处理的场景。通过避免所有这些,Node.js 实现了超过 100 万并发连接和超过 60 万并发 websockets 连接的可扩展性水平。

当然,存在在所有客户端请求之间共享单个线程的问题,这是编写 Node.js 应用程序的潜在陷阱。首先,繁重的计算可能会阻塞 Node 的单线程并导致所有客户端出现问题(稍后会详细介绍),因为传入请求将被阻塞,直到所述计算完成。其次,开发人员需要非常小心,不要让异常冒泡到核心(最顶层)Node.js 事件循环,这将导致 Node.js 实例终止(有效地使程序崩溃)。

用于避免异常冒泡到表面的技术是将错误作为回调参数传递回调用者(而不是像在其他环境中那样抛出它们)。即使某些未处理的异常设法冒泡,也已经开发了工具来监视 Node.js 进程并执行必要的崩溃实例恢复(尽管您可能无法恢复用户会话的当前状态),最常见的是 Forever 模块,或者使用不同的方法与外部系统工具 upstart 和 monit,甚至只是 upstart。

NPM:节点包管理器

在讨论 Node.js 时,绝对不应忽略的一件事是使用 NPM 对包管理的内置支持,NPM 是每个 Node.js 安装默认附带的工具。NPM 模块的思想与 Ruby Gems 的思想非常相似:一组公开可用、可重用的组件,可通过在线存储库轻松安装获得,具有版本和依赖项管理。

完整的打包模块列表可以在 npm 网站上找到,或者使用 npm CLI 工具访问,该工具自动随 Node.js 安装。模块生态系统对所有人开放,任何人都可以发布自己的模块,这些模块将列在 npm 存储库中。

今天一些最有用的 npm 模块是:

应该在何处使用 Node.js 的示例

聊天

聊天应用程序确实是 Node.js 的最佳示例:它是一个轻量级、高流量、数据密集型(但处理/计算量低)的应用程序,可跨分布式设备运行。它也是一个很好的学习用例,因为它很简单,但它涵盖了您将在典型 Node.js 应用程序中使用的大多数范例。

让我们试着描述它是如何工作的。

在最简单的例子中,我们的网站上有一个单独的聊天室,人们可以在那里以一对多(实际上是所有人)的方式交换消息。例如,假设我们在网站上有三个人都连接到我们的留言板。

在服务器端,我们有一个简单的 Express.js 应用程序,它实现了两件事:

  1. 一个 GET /请求处理程序,它为包含留言板和“发送”按钮的网页提供服务,以初始化新的消息输入,以及
  2. 一个 websockets 服务器,它监听 websocket 客户端发出的新消息。

在客户端,我们有一个 HTML 页面,其中设置了几个处理程序,一个用于“发送”按钮单击事件,它接收输入消息并将其发送到 websocket,另一个用于侦听在 websockets 客户端上(即其他用户发送的消息,服务器现在希望客户端显示这些消息)新传入的消息。

当其中一个客户端发布消息时,会发生以下情况:

  1. 浏览器通过 JavaScript 处理程序捕获“发送”按钮单击,从输入字段(即消息文本)中获取值,并使用连接到我们服务器的 websocket 客户端(在网页初始化时初始化)发出 websocket 消息。
  2. websocket 连接的服务器端组件接收消息并使用广播方法将其转发给所有其他连接的客户端。
  3. 所有客户端都通过在网页中运行的 websockets 客户端组件接收作为推送消息的新消息。然后他们选择消息内容并通过将新消息附加到板来就地更新网页。

image

这是最简单的例子。要获得更强大的解决方案,您可以使用基于 Redis 存储的简单缓存。或者在更高级的解决方案中,一个消息队列来处理消息到客户端的路由,以及一个更强大的传递机制,它可以覆盖临时连接丢失或为离线时注册客户端存储消息。但无论您做出何种改进,Node.js 仍将在相同的基本原则下运行:对事件做出反应、处理许多并发连接并保持用户体验的流畅性。

API ON TOP OF AN OBJECT DB

尽管 Node.js 在实时应用程序中非常出色,但它非常适合从对象数据库(例如 MongoDB)读取数据。JSON 存储数据允许 Node.js 在没有阻抗不匹配和数据转换的情况下运行。

使用 Node.js,您可以简单地使用 REST API 返回您的 JSON 对象以供客户端使用。此外,在从数据库读取或写入时,您无需担心在 JSON 和其他任何内容之间进行转换(如果您使用的是 MongoDB)。总之,您可以通过跨客户端、服务器和数据库使用统一的数据序列化格式来避免多次转换的需要。

排队输入

如果您正在接收大量并发数据,您的数据库可能会成为瓶颈。如上所示,Node.js 本身可以轻松处理并发连接。但是因为数据库访问是一个阻塞操作(在这种情况下),我们遇到了麻烦。解决方案是在数据真正写入数据库之前确认客户端的行为。

使用这种方法,系统可以在重负载下保持其响应能力,这在客户端不需要确认数据写入成功时特别有用。典型的例子包括:用户跟踪数据的记录或写入,批量处理,直到以后才使用;以及不需要立即反映的操作(例如更新 Facebook 上的“喜欢”计数),其中最终一致性(在 NoSQL 世界中经常使用)是可以接受的。

数据通过某种缓存或消息队列基础设施(如 RabbitMQ 或 ZeroMQ)排队,并由单独的数据库批量写入过程或计算密集型处理后端服务消化,这些服务在性能更好的平台上为此类任务编写。类似的行为可以用其他语言/框架实现,但不能在相同的硬件上实现,并具有相同的高吞吐量。

image

简而言之:使用 Node,您可以将数据库写入推到一边并稍后处理它们,就好像它们成功一样继续。

数据流

在更传统的 Web 平台中,HTTP 请求和响应被视为孤立事件;事实上,它们实际上是流。可以在 Node.js 中利用这种观察来构建一些很酷的功能。例如,可以在文件仍在上传时对其进行处理,因为数据是通过流传入的,我们可以以在线方式对其进行处理。这可以用于实时音频或视频编码,以及不同数据源之间的代理

代理人

Node.js 很容易用作服务器端代理,它可以以非阻塞方式处理大量同时连接。它对于代理具有不同响应时间的不同服务或从多个源点收集数据特别有用。

一个例子:考虑一个服务器端应用程序与第三方资源通信,从不同来源提取数据,或者将图像和视频等资产存储到第三方云服务。

尽管确实存在专用代理服务器,但如果您的代理基础设施不存在或者您需要本地开发的解决方案,则使用 Node 可能会有所帮助。我的意思是,您可以使用 Node.js 开发服务器为资产和代理/存根 API 请求构建客户端应用程序,而在生产中,您将使用专用代理服务(nginx、HAProxy 等)处理此类交互)

可以使用 Node.js 的地方

服务器端 Web 应用程序

Node.js 和 Express.js 也可用于在服务器端创建经典的 Web 应用程序。然而,尽管可能,Node.js 将携带呈现的 HTML 的这种请求 - 响应范式并不是最典型的用例。有赞成和反对这种方法的论据。以下是一些需要考虑的事实:

优点:

缺点:

[*] 这些 CPU 密集型计算的替代方案是创建一个具有后端处理的高度可扩展的 MQ 支持环境,以保持 Node 作为前端“职员”以异步处理客户端请求。

不应该使用 Node.js 的地方

繁重的服务器端计算/处理

当涉及到繁重的计算时,Node.js 并不是最好的平台。不,您绝对不想在 Node.js 中构建斐波那契计算服务器。一般来说,任何 CPU 密集型操作都会取消 Node 通过其事件驱动的非阻塞 I/O 模型提供的所有吞吐量优势,因为当线程被您的数字运算占用时,任何传入请求都将被阻塞 - 假设您正在尝试在响应请求的同一个 Node 实例中运行计算。

如前所述,Node.js 是单线程的,仅使用一个 CPU 内核。在多核服务器上添加并发性时,Node 核心团队正在以集群模块的形式完成一些工作 [参考:http://nodejs.org/api/cluster.html]。您还可以通过 nginx 在反向代理后面非常轻松地运行多个 Node.js 服务器实例

使用集群,您仍然应该将所有繁重的计算卸载到在更合适的环境中编写的后台进程,并让它们通过像 RabbitMQ 这样的消息队列服务器进行通信。

尽管您的后台处理最初可能在同一台服务器上运行,但这种方法具有非常高的可扩展性的潜力。这些后台处理服务可以轻松地分发到单独的工作服务器,而无需配置前端 Web 服务器的负载。

当然,您也可以在其他平台上使用相同的方法,但是使用 Node.js,您可以获得我们讨论过的高请求/秒吞吐量,因为每个请求都是一个非常快速有效地处理的小任务。

结论

我们已经从理论到实践讨论了 Node.js,从它的目标和抱负开始,到它的最佳点和陷阱结束。当人们遇到 Node 问题时,几乎总是归结为这样一个事实:阻塞操作是万恶之源——99% 的 Node 滥用都是直接后果。

请记住:Node.js 的创建从来不是为了解决计算扩展问题。它的创建是为了解决 I/O 扩展问题,它做得非常好。

为什么要使用 Node.js?如果您的用例不包含 CPU 密集型操作,也不访问任何阻塞资源,您可以利用 Node.js 的优势并享受快速且可扩展的网络应用程序。

如何正确学习 node.js

原文 如何正确的学习 Node.js
作者 狼叔