什么是节点.js?[已关闭]

2022-08-29 23:06:04

我不完全理解Node.js的全部内容。也许是因为我主要是一个基于Web的业务应用程序开发人员。它是什么,它有什么用?

到目前为止,我的理解是:

  1. 编程模型是事件驱动的,尤其是它处理 I/O 的方式。
  2. 它使用JavaScript,解析器是V8
  3. 它可以轻松用于创建并发服务器应用程序。

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


答案 1

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

Node.js在生产中绝对是可能的,但与文档似乎承诺的“交钥匙”部署相去甚远。使用Node.js v0.6.x,“集群”已经集成到平台中,提供了一个重要的构建块,但我的“生产.js”脚本仍然是大约150行逻辑来处理诸如创建日志目录,回收死工人等。对于“严肃”的生产服务,您还需要准备好限制传入连接并执行Apache为PHP所做的所有操作。公平地说,Ruby on Rails有这个确切的问题。它通过两种互补的机制来解决:1)将Ruby放在Rails/Node上.js专用Web服务器(用C编写并测试到地狱和后面)后面,如Nginx(或Apache / Lighttd)。Web 服务器可以高效地提供静态内容、访问日志记录、重写 URL、终止 SSL、强制实施访问规则以及管理多个子服务。对于命中实际节点服务的请求,Web 服务器代理请求。2)使用像Unicorn这样的框架,它将管理工作进程,定期回收它们等。我还没有找到一个Node.js服务框架,看起来完全成熟;它可能存在,但我还没有找到它,并且仍然在我手卷的“生产.js”中使用了大约150行。

阅读像Express这样的框架似乎标准的做法是通过一个万事通Node.js服务来提供一切服务......“app.use(express.static(__dirname + '/public'))”。对于低负载服务和开发,这可能很好。但是,一旦您尝试对服务施加大量时间负载并使其全天候运行,您很快就会发现推动大型站点拥有良好烘焙,强化的C代码(如Nginx)的动机,这些代码位于其站点前面并处理所有静态内容请求(...直到您设置了 CDN,如 Amazon CloudFront))。对于一个有点幽默和毫不掩饰的负面看法,看看这个家伙

Node.js也发现越来越多的非服务用途。即使您使用其他内容来提供Web内容,您仍然可以使用Node.js作为构建工具,使用npm模块来组织代码,Browserify将其拼接成单个资产,并使用uglify-js将其缩小以进行部署。对于处理Web,JavaScript是一个完美的阻抗匹配,并且经常使它成为最简单的攻击途径。例如,如果你想浏览一堆JSON响应有效负载,你应该使用我的下划线-CLI模块,这是结构化数据的实用程序带。

优点/缺点:

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

与简单的“每个请求一个进程”模型 (LAMP) 相比

  • 优点:可扩展到数千个活动连接。非常快速,非常高效。对于 Web 队列,这可能意味着所需的框数比 PHP 或 Ruby 减少了 10 倍。
  • 优点:编写并行模式很容易。想象一下,您需要从 Memcached 获取三个(或 N 个)blob。在 PHP 中执行此操作 ...你是否只是编写了代码来获取第一个 blob,然后是第二个 blob,然后是第三个 blob?哇,太慢了。有一个特殊的PECL模块可以解决Memcached的特定问题,但是如果您想与数据库查询并行获取一些Memcached数据怎么办?在 Node.js中,由于范例是异步的,因此让 Web 请求并行执行多项操作是非常自然的。
  • 缺点:异步代码从根本上说比同步代码更复杂,如果不充分了解并发执行的实际含义,开发人员就很难提前学习曲线。尽管如此,它比编写任何一种带有锁定功能的多线程代码要困难得多。
  • 缺点:例如,如果计算密集型请求运行100毫秒,它将停止处理在同一Node.js进程中处理的其他请求...又名,协作式多任务处理。这可以通过 Web Workers 模式(剥离子进程来处理成本高昂的任务)来缓解。或者,您可以使用大量的 Node.js 工作线程,只让每个工作线程同时处理单个请求(仍然相当高效,因为没有进程回收)。
  • 缺点:运行生产系统比Apache + PHP,PerlRubyCGI模型复杂得多。未处理的异常将降低整个过程,因此需要逻辑来重新启动失败的工作线程(请参阅群集)。具有错误本机代码的模块可能会使进程硬崩溃。每当一个工作线程死亡时,它处理的任何请求都会被删除,因此一个错误的API可以很容易地降低其他共同托管API的服务。

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

  • 优点:在 Node.js 中执行异步操作比在其他任何地方执行线程安全更容易,并且可以说提供了更大的好处。Node.js是迄今为止我工作过的最不痛苦的异步范式。使用好的库,它只比编写同步代码稍微困难一些。
  • 优点:没有多线程/锁定错误。没错,您可以预先投资编写更详细的代码,以表达适当的异步工作流,而无需阻止操作。你需要编写一些测试并让事情工作(它是一种脚本语言,胖手指变量名称只在单元测试时捕获)。但是,一旦你让它工作,heisenbugs的表面积 - 奇怪的问题,每百万次运行中只出现一次 - 这个表面积要低得多。税收写入Node.js代码被大量前加载到编码阶段。然后你倾向于得到稳定的代码。
  • 优点:JavaScript在表达功能方面更加轻量级。很难用文字来证明这一点,但是JSON,动态类型,lambda符号,原型继承,轻量级模块,无论什么......它只是倾向于使用更少的代码来表达相同的想法。
  • Con:也许你真的非常非常喜欢用Java编写代码服务?

有关JavaScript和Node.js的另一个观点,请查看从Java到Node.js,这是一篇关于Java开发人员学习Node.js的印象和经验的博客文章。


模块在考虑node时,请记住,您选择的JavaScript库将定义您的体验。大多数人至少使用两个,一个异步模式助手(Step,Futures,Async)和一个JavaScript糖模块(Underscore.js)。

Helper / JavaScript Sugar:

  • 下划线.js - 使用这个。只管去做。它通过 _.isString() 和 _.isArray() 等内容使您的代码变得美观且可读。我真的不确定否则你怎么能写出安全的代码。另外,对于增强的命令行 fu,请查看我自己的 Underscore-CLI

异步模式模块:

  • Step - 一种非常优雅的方式来表达串行和并行操作的组合。我个人的推荐。请参阅关于步骤代码内容的帖子。
  • 期货 - 更灵活(这真的是一件好事吗?)通过需求来表达订购的方式。可以表示诸如“并行开始a,b,c”之类的东西。当 A 和 B 完成时,启动 AB。当 A 和 C 完成时,启动 AC。这种灵活性需要更加小心,以避免工作流程中的错误(例如从不调用回调或多次调用回调)。请参阅Raynos关于使用期货的帖子(这是让我“获得”期货的帖子)。
  • 异步 - 更传统的库,每个模式都有一个方法。在我宗教皈依阶梯之前,我就开始了这一点,随后意识到Async中的所有模式都可以用一个更具可读性的范式在Step中表达。
  • TameJS - 由OKCupid编写,它是一个预编译器,添加了一个新的语言,主要“await”,以优雅地编写串行和并行工作流程。该模式看起来很棒,但它确实需要预先编译。我仍然在做这个决定。
  • StreamlineJS - TameJS的竞争对手。我倾向于驯服,但你可以自己下定决心。

或者要阅读有关异步库的所有信息,请参阅此与作者的小组访谈

网络框架:

  • 表达伟大的Ruby on Rails-esk框架,用于组织网站。它使用JADE作为XML / HTML模板引擎,这使得构建HTML变得不那么痛苦,甚至几乎是优雅的。
  • jQuery虽然从技术上讲不是一个节点模块,但jQuery正迅速成为客户端用户界面的事实标准。jQuery提供了类似CSS的选择器来“查询”DOM元素集,然后可以对其进行操作(集合处理程序,属性,样式等)。同样,Twitter的Bootstrap CSS框架,Backbone.js用于MVC模式,Browserify.js将所有JavaScript文件拼接成一个文件。这些模块都正在成为事实上的标准,所以如果你还没有听说过它们,你至少应该检查一下它们。

测试:

  • JSHint - 必须使用;起初我没有使用这个,现在似乎无法理解。JSLint添加了一堆使用Java等编译语言获得的基本验证。不匹配的括号、未声明的变量、许多形状和大小的类型。你还可以打开各种形式的我称之为“肛门模式”,在那里你可以验证空格的风格等等,如果这是你的一杯茶,这是可以的 - 但真正的价值来自获得关于你忘记结束“)”的确切行号的即时反馈......而不必运行代码并击中违规行。“JSHint”是Douglas CrockfordJSLint的更可配置的变体。
  • 摩卡是我开始更喜欢的誓言的竞争对手。这两个框架都很好地处理了基础知识,但复杂的模式往往更容易在摩卡中表达。
  • 誓言誓言真的非常优雅。它打印出一个可爱的报告(--spec),显示哪些测试用例通过/失败。花30分钟学习它,你可以毫不费力地为你的模块创建基本测试。
  • Zombie - 使用JSDom作为虚拟“浏览器”的HTML和JavaScript的无头测试。非常强大的东西。将其与重播相结合,以获得对浏览器内代码的快速确定性测试。
  • 关于如何“思考”测试的评论:
    • 测试不是可选的。对于像JavaScript这样的动态语言,很少有静态检查。例如,将两个参数传递给需要 4 的方法,在执行代码之前不会中断。在JavaScript中创建错误的门槛非常低。基本测试对于弥补与编译语言的验证差距至关重要。
    • 忘记验证,只需让代码执行即可。对于每种方法,我的第一个验证案例是“没有任何中断”,这是最常触发的情况。证明您的代码在不抛出的情况下运行会捕获80%的错误,并且将大大提高您的代码信心,以至于您会发现自己会返回并添加您跳过的细微的验证案例。
    • 从小处着手,打破惯性障碍。我们都很懒惰,时间紧迫,很容易将测试视为“额外的工作”。所以从小事做起。编写测试用例 0 - 加载模块并报告成功。如果你强迫自己做这么多,那么测试的惯性障碍就被打破了。这是第一次<30分钟,包括阅读文档。现在编写测试用例 1 - 调用其中一个方法并验证“没有任何中断”,也就是说,您不会收到错误。测试用例 1 应花费不到一分钟的时间。随着惯性消失,您可以轻松地逐步扩展测试覆盖范围。
    • 现在,使用代码改进测试。不要被模拟服务器等“正确”的端到端测试所吓倒。代码从简单开始,然后发展到处理新情况;测试也应该如此。向代码添加新用例和新复杂性时,请添加测试用例来执行新代码。当您发现错误时,添加验证和/或新案例以覆盖有缺陷的代码。当您正在调试并对一段代码失去信心时,请返回并添加测试以证明它正在执行您认为它正在执行的操作。捕获示例数据的字符串(来自您调用的其他服务,您抓取的网站等),并将它们提供给您的解析代码。这里有几个案例,那里改进了验证,你最终会得到高度可靠的代码。

另外,请查看推荐的Node.js模块的官方列表。但是,GitHub的Node Modules Wiki更加完整,并且是一个很好的资源。


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

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

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

节点.js是单线程且无锁的。Node.js,作为一个非常深思熟虑的设计选择,每个进程只有一个线程。因此,多个线程根本不可能同时访问数据。因此,不需要锁。线程很难。真的很难。如果你不相信这一点,你就没有做足够的线程编程。正确锁定是很困难的,并且会导致很难跟踪的错误。消除锁和多线程使得最讨厌的错误类别之一就消失了。这可能是节点的最大优势。

但是,如何利用我的 16 核机箱呢?

两种方式:

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

Node.js可以让你做一些非常强大的事情,而不会出汗。假设你有一个Node.js程序,它执行各种任务,在TCP端口上侦听命令,编码一些图像等等。通过五行代码,可以添加基于 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");

Now you can hit a URL and check the status of your running process. Add a few buttons, and you have a "management portal". If you have a running Perl / Python / Ruby script, just "throwing in a management portal" isn't exactly simple.

But isn't JavaScript slow / bad / evil / spawn-of-the-devil? JavaScript has some weird oddities, but with "the good parts" there's a very powerful language there, and in any case, JavaScript is THE language on the client (browser). JavaScript is here to stay; other languages are targeting it as an IL, and world class talent is competing to produce the most advanced JavaScript engines. Because of JavaScript's role in the browser, an enormous amount of engineering effort is being thrown at making JavaScript blazing fast. V8 is the latest and greatest javascript engine, at least for this month. It blows away the other scripting languages in both efficiency AND stability (looking at you, Ruby). And it's only going to get better with huge teams working on the problem at Microsoft, Google, and Mozilla, competing to build the best JavaScript engine (It's no longer a JavaScript "interpreter" as all the modern engines do tons of JIT compiling under the hood with interpretation only as a fallback for execute-once code). Yeah, we all wish we could fix a few of the odder JavaScript language choices, but it's really not that bad. And the language is so darn flexible that you really aren't coding JavaScript, you are coding Step or jQuery -- more than any other language, in JavaScript, the libraries define the experience. To build web applications, you pretty much have to know JavaScript anyway, so coding with it on the server has a sort of skill-set synergy. It has made me not dread writing client code.

Besides, if you REALLY hate JavaScript, you can use syntactic sugar like CoffeeScript. Or anything else that creates JavaScript code, like Google Web Toolkit (GWT).

Speaking of JavaScript, what's a "closure"? - Pretty much a fancy way of saying that you retain lexically scoped variables across call chains. ;) Like this:

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();

See how you can just use "myData" without doing anything awkward like stashing it into an object? And unlike in Java, the "myData" variable doesn't have to be read-only. This powerful language feature makes asynchronous-programming much less verbose and less painful.

Writing asynchronous code is always going to be more complex than writing a simple single-threaded script, but with Node.js, it's not that much harder and you get a lot of benefits in addition to the efficiency and scalability to thousands of concurrent connections...


答案 2

I think the advantages are:

  1. Web development in a dynamic language (JavaScript) on a VM that is incredibly fast (V8). It is much faster than Ruby, Python, or Perl.

  2. Ability to handle thousands of concurrent connections with minimal overhead on a single process.

  3. JavaScript is perfect for event loops with first class function objects and closures. People already know how to use it this way having used it in the browser to respond to user initiated events.

  4. A lot of people already know JavaScript, even people who do not claim to be programmers. It is arguably the most popular programming language.

  5. Using JavaScript on a web server as well as the browser reduces the impedance mismatch between the two programming environments which can communicate data structures via JSON that work the same on both sides of the equation. Duplicate form validation code can be shared between server and client, etc.