什么是 Node.js? [关闭]

2024-01-05

我不完全明白什么Node.js http://en.wikipedia.org/wiki/Node.js就是这样。也许是因为我主要是一个基于网络的业务应用程序开发人员。它是什么以及它有什么用?

到目前为止我的理解是:

  1. 编程模型是事件驱动的,尤其是它处理的方式I/O http://en.wikipedia.org/wiki/Input/output.
  2. It uses JavaScript http://en.wikipedia.org/wiki/JavaScript解析器是V8 http://en.wikipedia.org/wiki/V8_%28JavaScript_engine%29.
  3. 它可以轻松地用于创建并发服务器应用程序。

我的理解正确吗?如果是,那么事件 I/O 的好处是什么?它只是为了并发性吗?另外,Node.js 的方向是成为一个类似基于 JavaScript(基于 V8)的编程模型的框架吗?


我在工作中使用 Node.js,发现它非常强大。被迫选择一个词来描述 Node.js,我会说“有趣”(这不是一个纯粹的正面形容词)。社区充满活力并不断发展。尽管 JavaScript 很奇怪,但它仍然是一种很棒的编码语言。而且您每天都会重新思考自己对“最佳实践”和结构良好的代码模式的理解。现在有大量的想法涌入 Node.js,在其中工作会让你接触到所有这些想法 - 伟大的精神举重。

Node.js 在生产中肯定是可能的,但距离文档中看似承诺的“交钥匙”部署还很远。在 Node.js v0.6.x 中,“集群”已集成到平台中,提供了基本的构建块之一,但我的“Production.js”脚本仍然有约 150 行逻辑来处理创建日志等内容对于“严肃的”生产服务,您还需要准备好限制传入连接并执行 Apache 所做的所有操作PHP http://en.wikipedia.org/wiki/PHP。公平起见,红宝石 on Rails http://en.wikipedia.org/wiki/Ruby_on_Rails有这个exact问题。它是通过两种互补机制解决的:1)将 Ruby on Rails/Node.js 放在专用的 Web 服务器后面(用 C 编写并经过反复测试),例如Nginx http://en.wikipedia.org/wiki/Nginx (or Apache http://en.wikipedia.org/wiki/Apache_HTTP_Server / Lighttd http://en.wikipedia.org/wiki/Lighttpd)。 Web 服务器可以有效地提供静态内容、访问日志记录、重写 URL、终止SSL http://en.wikipedia.org/wiki/SSL、执行访问规则并管理多个子服务。对于命中实际节点服务的请求,Web 服务器会代理该请求。 2)使用类似的框架Unicorn http://unicorn.bogomips.org/它将管理工作进程,定期回收它们,等等。我还没有找到一个看起来完全成熟的 Node.js 服务框架;它可能存在,但我还没有找到它,并且仍在我的手卷“生产.js”中使用〜150行。

阅读框架如Express http://expressjs.com/看起来标准做法是通过一个万能的 Node.js 服务来提供一切服务......“app.use(express.static(__dirname + '/public'))”。对于低负载服务和开发来说,这可能没问题。但是,一旦您尝试在服务上投入大量时间并让它 24/7 运行,您很快就会发现推动大型网站拥有精心烘焙、强化的 C 代码的动机,例如Nginx http://en.wikipedia.org/wiki/Nginx前端他们的网站并处理所有静态内容请求(......直到您设置一个CDN http://en.wikipedia.org/wiki/Content_delivery_network, like 亚马逊云前 http://en.wikipedia.org/wiki/Amazon_CloudFront))。对于这个有点幽默和毫不掩饰的负面看法,请参阅this guy http://teddziuba.com/2011/10/node-js-is-cancer.html.

Node.js 也发现了越来越多的非服务用途。即使您使用其他东西来提供 Web 内容,您仍然可以使用 Node.js 作为构建工具,使用npm http://en.wikipedia.org/wiki/Npm_%28software%29组织代码的模块,浏览器化 https://github.com/substack/node-browserify将其拼接成单个资产,并且uglify-js https://github.com/mishoo/UglifyJS缩小它以进行部署。对于处理网络,JavaScript 是一个完美的选择阻抗匹配 http://en.wikipedia.org/wiki/Object-relational_impedance_mismatch这通常使其成为最简单的攻击途径。例如,如果你想卑躬屈膝地通过一堆JSON http://en.wikipedia.org/wiki/JSON响应有效负载,你应该使用我的下划线-CLI https://github.com/ddopson/underscore-cli模块,结构化数据的实用带。

优点缺点:

  • 优点:对于服务器人员来说,在后端编写 JavaScript 是学习现代 UI 模式的“入门药物”。我不再害怕编写客户端代码。
  • 优点:倾向于鼓励正确的错误检查(几乎所有回调都会返回 err,困扰程序员去处理它;此外,async.js 和其他库处理“如果这些子任务中的任何一个失败,则失败”范例比典型的同步代码要好得多)
  • 优点:一些有趣且通常困难的任务变得微不足道 - 例如获取运行中任务的状态、工作人员之间的通信或共享缓存状态
  • 优点:庞大的社区和大量基于可靠的包管理器(npm)的优秀库
  • 缺点:JavaScript 没有标准库。您已经习惯了导入功能,以至于当您使用 JSON.parse 或其他不需要添加 npm 模块的内置方法时,会感觉很奇怪。这意味着一切都有五个版本。如果您对默认实现不满意,甚至 Node.js“核心”中包含的模块还有五个变体。这导致了快速的演变,但也带来了一定程度的混乱。

与简单的每个请求一个进程模型(LAMP http://en.wikipedia.org/wiki/LAMP_%28software_bundle%29):

  • 优点:可扩展到数千个活动连接。非常快而且非常高效。对于 Web 队列来说,这可能意味着与 PHP 或 Ruby 相比,所需的盒子数量减少了 10 倍
  • 优点:编写并行模式很容易。想象一下,您需要从以下位置获取三个(或 N)个 blob内存缓存 http://en.wikipedia.org/wiki/Memcached。在 PHP 中执行此操作...您是否刚刚编写了获取第一个 blob、然后是第二个、然后是第三个 blob 的代码?哇,那太慢了。有一个特别的PECL http://en.wikipedia.org/wiki/PEAR#PECL模块来解决 Memcached 的特定问题,但是如果您想与数据库查询并行获取一些 Memcached 数据怎么办?在 Node.js 中,由于范例是异步的,因此让一个 Web 请求并行执行多个操作是很自然的。
  • 缺点:异步代码从根本上来说比同步代码更复杂,如果开发人员没有充分理解并发执行的实际含义,那么前期的学习曲线可能会很困难。尽管如此,它比编写任何类型的带锁定的多线程代码要困难得多。
  • 缺点:如果一个计算密集型请求运行了例如 100 毫秒,它将停止处理同一 Node.js 进程中正在处理的其他请求...又名,合作多任务处理 http://en.wikipedia.org/wiki/Computer_multitasking#Cooperative_multitasking.2Ftime-sharing。这可以通过 Web Workers 模式来缓解(剥离子流程来处理昂贵的任务)。或者,您可以使用大量 Node.js 工作线程,并且只让每个工作线程同时处理一个请求(仍然相当高效,因为没有进程回收)。
  • 缺点:运行生产系统比运行系统复杂得多CGI http://en.wikipedia.org/wiki/Common_Gateway_InterfaceApache + PHP 等模型,Perl http://en.wikipedia.org/wiki/Perl, Ruby http://en.wikipedia.org/wiki/Ruby_%28programming_language%29等。未处理的异常将导致整个进程崩溃,需要逻辑来重新启动失败的工作线程(请参阅cluster http://nodejs.org/docs/latest/api/cluster.html)。具有错误的本机代码的模块可能会导致进程严重崩溃。每当一个工作进程死亡时,它正在处理的任何请求都会被丢弃,因此一个有缺陷的 API 很容易降低其他共同托管的 API 的服务质量。

与用 Java / C# / C 编写“真正的”服务(C?真的吗?)

  • 优点:在 Node.js 中执行异步比在其他地方执行线程安全更容易,并且可以说提供了更大的好处。 Node.js 是迄今为止我所使用过的最不痛苦的异步范例。有了好的库,它只比编写同步代码稍微困难一点。
  • 优点:没有多线程/锁定错误。确实,您需要预先编写更详细的代码来表达没有阻塞操作的正确异步工作流程。您需要编写一些测试并使其正常工作(它是一种脚本语言,并且胖手指变量名称仅在单元测试时捕获)。但是,一旦你让它工作,表面积海森巴格 http://en.wikipedia.org/wiki/Heisenbug——奇怪的问题在一百万次运行中才会出现一次——表面积要小得多。编写 Node.js 代码的税收大量提前加载到编码阶段。然后你往往会得到稳定的代码。
  • 优点:JavaScript 在表达功能方面更加轻量级。很难用言语来证明这一点,但是JSON http://en.wikipedia.org/wiki/JSON、动态类型、lambda 表示法、原型继承、轻量级模块等等……它只是往往需要更少的代码来表达相同的想法。
  • 缺点:也许您真的非常喜欢 Java 编码服务?

有关 JavaScript 和 Node.js 的另一个视角,请查看从 Java 到 Node.js http://n0tw0rthy.wordpress.com/2012/01/08/from-java-to-node-js/,一篇关于 Java 开发人员学习 Node.js 的印象和经验的博客文章。


Modules在考虑 Node 时,请记住您选择的 JavaScript 库将DEFINE你的经验。大多数人至少使用两个,一个异步模式助手(Step、Futures、Async)和一个 JavaScript 糖模块(下划线.js http://en.wikipedia.org/wiki/Underscore.js).

帮助程序/JavaScript 糖:

  • 下划线.js http://documentcloud.github.com/underscore/- 用这个。去做就对了。它使您的代码变得漂亮且可读,例如 _.isString() 和 _.isArray()。我不太确定否则如何编写安全代码。另外,对于增强的命令行功能,请查看我自己的下划线-CLI https://github.com/ddopson/underscore-cli.

异步模式模块:

  • Step https://github.com/creationix/step- 一种非常优雅的方式来表达串行和并行操作的组合。我个人的推荐。看my post https://stackoverflow.com/questions/5989538/nodejs-parallel-callback-design-pattern/7167192#7167192步骤代码是什么样的。
  • Futures https://github.com/coolaj86/futures- 通过需求表达排序的更灵活(这真的是一件好事吗?)的方式。可以表达“并行启动a、b、c。当A、B完成后,启动AB。当A、C完成后,启动AC。”这种灵活性需要更加小心,以避免工作流程中出现错误(例如从不调用回调,或多次调用它)。看雷诺斯的帖子 https://stackoverflow.com/questions/5989538/nodejs-parallel-callback-design-pattern/5994989#5994989关于使用期货(这是让我“得到”期货的帖子)。
  • Async https://github.com/caolan/async- 更传统的库,每种模式都有一种方法。在我虔诚地转向 Step 之前,我就开始了这一点,随后意识到 Async 中的所有模式都可以用一个更具可读性的范例在 Step 中表达。
  • TameJS http://tamejs.org/- 由 OKCupid 编写,它是一个预编译器,添加了新的语言原语“await”,用于优雅地编写串行和并行工作流程。该模式看起来很棒,但它确实需要预编译。关于这一点我还在拿定主意。
  • StreamlineJS https://github.com/Sage/streamlinejs- TameJS 的竞争对手。我倾向于Tame,但你可以自己做决定。

或者要阅读有关异步库的所有内容,请参阅本次小组访谈 http://www.infoq.com/articles/surviving-asynchronous-programming-in-javascript与作者们。

网络框架:

  • Express https://github.com/visionmedia/express用于组织网站的出色 Ruby on Rails-esk 框架。它用JADE http://jade-lang.com/作为一个 XML/HTML 模板引擎,它使得构建 HTML 变得不再那么痛苦,甚至几乎是优雅的。
  • jQuery http://api.jquery.com/category/selectors/虽然从技术上讲,jQuery 不是一个节点模块,但它正在迅速成为客户端用户界面的事实上的标准。 jQuery 提供类似 CSS 的选择器来“查询”DOM 元素集,然后对其进行操作(设置处理程序、属性、样式等)。沿着同样的思路,Twitter引导程序 http://twitter.github.com/bootstrap/CSS框架,骨干网.js http://documentcloud.github.com/backbone/ for an MVC http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller图案,以及Browserify.js https://github.com/substack/node-browserify将所有 JavaScript 文件拼接到一个文件中。这些模块都正在成为事实上的标准,因此如果您还没有听说过它们,您至少应该检查一下它们。

Testing:

  • JSHint http://github.com/jshint/node-jshint- 必须使用;我一开始没有使用这个,现在看来难以理解。 JSLint 添加了使用 Java 等编译语言获得的一系列基本验证。括号不匹配、未声明的变量、多种形状和大小的打字。您还可以打开我所说的“肛门模式”的各种形式,您可以在其中验证空白的样式和诸如此类的东西,如果这是您喜欢的,那就没问题了——但真正的价值来自于获得有关确切行号的即时反馈,其中你忘记了结束符“)”......而不必运行你的代码并点击有问题的行。 “JSHint”是一个更可配置的变体道格拉斯·克罗克福德 https://rads.stackoverflow.com/amzn/click/com/0596517742's JSLint http://www.jslint.com/.
  • Mocha http://visionmedia.github.com/mocha/我开始更喜欢誓言的竞争对手。这两个框架都能很好地处理基础知识,但复杂的模式往往更容易在 Mocha 中表达。
  • Vows http://vowsjs.org/誓言确实很优雅。它会打印出一份可爱的报告(--spec),显示哪些测试用例通过/失败。花 30 分钟学习它,您就可以轻松地为您的模块创建基本测试。
  • Zombie https://github.com/assaf/zombie- 使用 HTML 和 JavaScript 进行无头测试JSDom https://github.com/tmpvar/jsdom/作为虚拟“浏览器”。非常强大的东西。将其与Replay https://github.com/assaf/node-replay#readme对浏览器内代码进行闪电般快速的确定性测试。
  • A comment on how to "think about" testing:
    • 测试是非可选的。对于像 JavaScript 这样的动态语言,有very一些静态检查。例如,将两个参数传递给需要 4 个参数的方法在执行代码之前不会中断。在 JavaScript 中创建错误的门槛相当低。基本测试对于弥补编译语言的验证差距至关重要。
    • 忘记验证,只需让代码执行即可。对于每种方法,我的第一个验证案例是“没有任何问题”,这是最常触发的情况。证明您的代码运行时不会抛出异常,可以捕获 80% 的错误,并且可以极大地提高您的代码信心,您会发现自己要返回并添加您跳过的细致入微的验证案例。
    • 从小事做起,打破惯性障碍。我们都很懒,时间紧迫,很容易将测试视为“额外的工作”。所以从小事做起。编写测试用例 0 - 加载模块并报告成功。如果你强迫自己做这么多,那么测试的惯性障碍就被打破了。第一次执行此操作只需不到 30 分钟,包括阅读文档。现在编写测试用例 1 - 调用您的方法之一并验证“没有中断”,也就是说,您没有收到错误。测试用例 1 应该花费不到一分钟的时间。随着惯性的消失,逐步扩大测试覆盖范围变得很容易。
    • 现在用您的代码改进您的测试。不要被模拟服务器等“正确的”端到端测试的样子吓到。代码从简单开始,逐渐发展以处理新情况;测试也应该如此。当您向代码添加新案例和新复杂性时,请添加测试案例来测试新代码。当您发现错误时,添加验证和/或新案例以覆盖有缺陷的代码。当您在调试并对一段代码失去信心时,请返回并添加测试以证明它正在按照您的想法进行。捕获示例数据字符串(来自您调用的其他服务、您抓取的网站等)并将其提供给您的解析代码。这里有一些案例,那里改进了验证,您最终将得到高度可靠的代码。

另外,请查看官方名单 http://nodejs.org/docs/v0.4.11/api/appendix_1.html推荐的 Node.js 模块。然而,GitHub's http://en.wikipedia.org/wiki/GitHub 节点模块维基 https://github.com/joyent/node/wiki/modules更完整并且是一个很好的资源。


要理解 Node,考虑一些关键的设计选择会很有帮助:

Node.js 是基于事件 and 异步 / 非阻塞。事件(例如传入的 HTTP 连接)将触发 JavaScript 函数,该函数执行一些工作并启动其他异步任务,例如连接到数据库或从另一台服务器提取内容。一旦这些任务开始,事件函数就会完成,Node.js 将返回睡眠状态。一旦发生其他事情,例如建立数据库连接或外部服务器响应内容,回调函数就会触发,并且执行更多 JavaScript 代码,可能会启动更多异步任务(例如数据库查询)。通过这种方式,Node.js 将愉快地为多个并行工作流程交错活动,运行任何在任何时间点都畅通无阻的活动。这就是 Node.js 在管理数千个并发连接方面做得如此出色的原因。

为什么不像其他人一样为每个连接使用一个进程/线程呢?在 Node.js 中,一个新连接只是一个非常小的堆分配。启动一个新进程需要显着更多的内存,在某些平台上需要兆字节。但真正的成本是与上下文切换相关的开销。当您有 10^6 个内核线程时,内核必须做大量工作来确定下一个应该执行的人。为 Linux 构建 O(1) 调度程序已经做了很多工作,但最终,单个事件驱动进程比竞争 CPU 时间的 10^6 个进程要高效得多。此外,在过载情况下,多进程模型的表现非常糟糕,导致关键的管理和管理服务匮乏,尤其是 SSHD(这意味着您甚至无法登录到盒子来弄清楚它到底有多糟糕)。

Node.js 是单线程 and 自由锁定。 Node.js 作为一个经过深思熟虑的设计选择,每个进程只有一个线程。因此,多个线程根本不可能同时访问数据。因此,不需要锁。线程很难。真的真的很难。如果您不相信这一点,那么您还没有进行足够的线程编程。正确锁定是很困难的,并且会导致很难追踪的错误。消除锁和多线程可以让最令人讨厌的错误之一消失。这可能是节点的最大优势。

但我该如何利用我的 16 核盒子呢?

两种方式:

  1. 对于图像编码等大型计算任务,Node.js 可以启动子进程或向其他工作进程发送消息。在此设计中,您将有一个线程管理事件流,N 个进程执行繁重的计算任务并占用其他 15 个 CPU。
  2. 为了扩展 Web 服务的吞吐量,您应该在一个机器上运行多个 Node.js 服务器,每个核心一个,使用cluster http://nodejs.org/api/cluster.html(对于 Node.js v0.6.x,此处链接的官方“集群”模块取代了具有不同 API 的 learnboost 版本)。然后,这些本地 Node.js 服务器可以在套接字上竞争以接受新连接,从而平衡它们之间的负载。一旦连接被接受,它就会紧密地绑定到这些共享进程中的一个。从理论上讲,这听起来很糟糕,但实际上它工作得很好,并且可以让您避免编写线程安全代码的麻烦。此外,这意味着 Node.js 具有出色的 CPU 缓存亲和性,可以更有效地使用内存带宽。

Node.js 让您可以毫不费力地完成一些非常强大的事情。假设您有一个 Node.js 程序,它执行各种任务,侦听TCP http://en.wikipedia.org/wiki/Transmission_Control_Protocol命令端口,编码一些图像,等等。通过五行代码,您可以添加基于 HTTP 的 Web 管理门户,该门户显示活动任务的当前状态。这很容易做到:

var http = require('http');
http.createServer(function (req, res) {
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.end(myJavascriptObject.getSomeStatusInfo());
}).listen(1337, "127.0.0.1");

现在您可以点击 URL 并检查正在运行的进程的状态。添加几个按钮,您就拥有了一个“管理门户”。如果您有一个正在运行的 Perl / Python / Ruby 脚本,那么仅仅“投入管理门户”并不那么简单。

但是 JavaScript 不是很慢/很糟糕/很邪恶/是魔鬼的衍生者吗?JavaScript 有一些奇怪的地方,但凭借“好的部分”,它是一种非常强大的语言,无论如何,JavaScript 是客户端(浏览器)上的语言。 JavaScript 将会继续存在;其他语言将其作为 IL 的目标,世界一流的人才正在竞相开发最先进的 JavaScript 引擎。由于 JavaScript 在浏览器中的作用,人们投入了大量的工程工作来使 JavaScript 变得极快。V8 http://en.wikipedia.org/wiki/V8_%28JavaScript_engine%29是最新、最好的 JavaScript 引擎,至少本月是这样。它在效率和稳定性方面都击败了其他脚本语言(看看你,Ruby)。只有微软、谷歌和 Mozilla 的庞大团队致力于解决这个问题,竞相构建最好的 JavaScript 引擎(它不再是 JavaScript“解释器”,因为所有现代引擎都会做大量的事情),它才会变得更好。JIT http://en.wikipedia.org/wiki/Just-in-time_compilation在后台进行编译,并进行解释,仅作为执行一次代码的后备)。是的,我们都希望能够修复一些更奇怪的 JavaScript 语言选择,但这实际上并没有那么糟糕。而且这种语言非常灵活,您实际上不是在编写 JavaScript,而是在编写 Step 或 jQuery——在 JavaScript 中,库比任何其他语言都更能定义体验。要构建 Web 应用程序,您几乎必须了解 JavaScript,因此在服务器上使用它进行编码具有某种技能组合的协同作用。它让我不再害怕编写客户端代码。

此外,如果你真的讨厌 JavaScript,你可以使用语法糖,比如咖啡脚本 http://jashkenas.github.com/coffee-script/。或者任何其他创建 JavaScript 代码的东西,比如Google 网络工具包 http://code.google.com/webtoolkit/ (GWT).

说到 JavaScript,什么是“闭包”?- 几乎是一种奇特的方式来表示您在调用链中保留词法范围的变量。 ;) 像这样:

var myData = "foo";
database.connect( 'user:pass', function myCallback( result ) {
    database.query("SELECT * from Foo where id = " + myData);
} );
// Note that doSomethingElse() executes _BEFORE_ "database.query" which is inside a callback
doSomethingElse();

看看如何只使用“myData”而不做任何尴尬的事情,例如将其存储到对象中?与 Java 不同的是,“myData”变量不必是只读的。这种强大的语言功能使异步编程变得不再那么冗长和痛苦。

编写异步代码总是比编写简单的单线程脚本更复杂,但是使用 Node.js,这并没有那么困难,除了对数千个并发连接的效率和可扩展性之外,您还可以获得很多好处。 ..

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

什么是 Node.js? [关闭] 的相关文章