一尘不染

什么是Node.js?[关闭]

javascript

我没有完全了解Node.js的全部含义。也许是因为我主要是基于Web的业务应用程序开发人员。它是什么,有什么用?

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

  1. 编程模型是事件驱动的,尤其是它处理I / O的方式。
  2. 它使用JavaScript,解析器为V8。
  3. 它可以轻松用于创建并发服务器应用程序。
    我的理解正确吗?如果是,那么事件I / O的好处是什么,并发性东西还有更多好处吗?另外,Node.js的发展方向是否像基于JavaScript(基于V8)的编程模型一样?

阅读 257

收藏
2020-04-25

共2个答案

一尘不染

我认为优点是:

  1. 在VM(V8)上以惊人的速度以动态语言(JavaScript)进行Web开发。它比Ruby,Python或Perl快得多。

  2. 能够在单个进程上以最小的开销处理数千个并发连接。

  3. JavaScript非常适合具有一流函数对象和闭包的事件循环。人们已经知道如何以这种方式使用它,并且已经在浏览器中使用它来响应用户启动的事件。

  4. 许多人已经知道JavaScript,甚至不自称是程序员的人也是如此。它可以说是最流行的编程语言。

  5. 在网络服务器和浏览器上使用JavaScript可以减少两个编程环境之间的阻抗失配,这两个编程环境可以通过JSON传递数据结构,在等式的两边都相同。可以在服务器和客户端等之间共享重复的表单验证代码。

2020-04-25
一尘不染

我在工作中使用Node.js,发现它非常强大。我不得不选择一个词来描述Node.js,我会说“有趣”(这不是纯粹的肯定形容词)。社区充满活力且不断发展。尽管有很多奇怪的地方,JavaScript仍然可以成为一种很好的编程语言。而且,你每天都会重新考虑对“最佳实践”和结构良好的代码模式的理解。目前,Node.js中涌现出大量的想法,并且在其中进行工作会使你暴露出所有这些想法-极大的心理举重。

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

像Express这样的阅读框架使标准做法看起来像是通过一项千篇一律的Node.js服务来提供所有服务……“ app.use(express.static(__ dirname +’/ public’))” 。对于较低负载的服务和开发,可能很好。但是,一旦你尝试将大量时间加载到服务上并使其以24/7的速度运行,你就会很快发现促使大站点进行良好烘焙,强化的C代码(例如Nginx)在其站点前处理所有内容的动机。静态内容请求(…直到你设置CDN,如Amazon CloudFront)。要对此采取一些幽默而毫不掩饰的否定态度,请参阅此人。

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

优点缺点:

  • 优点:对于服务器人员来说,在后端编写JavaScript一直是学习现代UI模式的“门户药物”。我不再害怕编写客户端代码。
  • 优点:倾向于鼓励进行正确的错误检查(几乎所有回调都会返回err,让程序员烦恼;相反,async.js和其他库处理“如果这些子任务中的任何一个失败,则失败”范例比典型的同步代码要好得多) )
  • 优点:一些有趣且通常是艰巨的任务变得微不足道-例如获取正在执行的任务的状态,工作人员之间的通信或共享缓存状态
  • Pro:基于可靠的软件包管理器(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进程中处理的其他请求的处理停顿… AKA,合作多任务。可以使用Web Workers模式(分拆子流程来处理昂贵的任务)来缓解这种情况。或者,你可以使用大量的Node.js工作程序,并且仅让每个人同时处理一个请求(由于没有进程回收,因此效率仍然很高)。
  • 缺点:运行生产系统比使用Apache + PHP,Perl,Ruby等CGI模型要复杂得多。未处理的异常将使整个过程中断,从而需要逻辑来重新启动失败的工作程序(请参阅cluster)。带有错误本机代码的模块可能会导致进程崩溃。每当工作人员死亡时,它正在处理的所有请求都将被丢弃,因此一个错误的API可能会轻易降低其他共同托管API的服务。
    与用Java / C#/ C(“ C”是真的吗?)编写“真实”服务相对
  • 优点:在Node.js中执行异步操作比在其他任何地方执行线程安全操作都容易,并且可以带来更大的好处。到目前为止,Node.js是我所经历过的最痛苦的异步范例。有了好的库,它只比编写同步代码难一点。
    优点:没有多线程/锁定错误。的确,你会先投入更多精力编写更详细的代码,这些代码表示没有阻塞操作的正确异步工作流程。并且你需要编写一些测试并使它正常工作(这是一种脚本语言,而且胖指法变量名仅在单元测试时捕获)。但是,一旦它开始工作,海森bug的表面积(仅在百万次运行中才会出现的奇怪问题)的表面积要低得多。编写Node.js代码的工作量是编码阶段的重中之重。然后,你往往会得到稳定的代码。
  • 优点:JavaScript的功能更加轻巧。很难用单词来证明这一点,但是JSON,动态类型,lambda表示法,原型继承,轻量级模块等等……表达相同的想法只会花费更少的代码。
    骗子:也许你真的非常喜欢Java编码服务?
    有关JavaScript和Node.js的另一种观点,请查看从Java到Node.js,这是一篇有关Java开发人员的印象和学习Node.js经验的博客文章。

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

助手/ JavaScript Sugar:

  • Underscore.js-使用它。去做就对了。使用_.isString()和_.isArray()之类的东西,可以使你的代码更加美观和可读。我不太确定你是否可以编写安全的代码。另外,要增强命令行功能,请查看我自己的Underscore-CLI。
    异步模式模块:

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

网络框架:

  • 表达用于组织网站的Great Ruby on Rails-esk框架。它使用JADE作为XML / HTML模板引擎,这使构建HTML的痛苦减轻了很多,甚至变得优雅。
  • jQuery虽然从技术上讲不是节点模块,但jQuery迅速成为客户端用户界面的实际标准。jQuery提供了类似于CSS的选择器来“查询”可以随后操作的DOM元素集(集合处理程序,属性,样式等)。同样,Twitter的Bootstrap CSS框架,用于MVC模式的Backbone.js和Browserify.js可以将所有JavaScript文件缝合到一个文件中。这些模块都已成为事实上的标准,因此如果你没有听说过它们,则至少应检查一下它们。
    测试:

  • JSHint-必须使用;我一开始没有使用它,现在似乎无法理解。JSLint重新添加了你使用Java之类的编译语言获得的许多基本验证。括号不匹配,未声明的变量,许多形状和大小的类型。你还可以启用各种形式的所谓的“肛门模式”,在其中验证空白样式和其他样式,如果那是你的茶,就可以了-但真正的价值来自于获得有关确切行号的即时反馈你忘记了结束“)” …而不必运行你的代码并点击令人讨厌的行。“ JSHint”是Douglas Crockford的JSLint的更可配置的变体。

  • 我开始偏爱Vows的Mocha竞争对手。两种框架都能很好地处理基础知识,但是复杂的模式在Mocha中往往更容易表达。
  • 誓言誓言真的很优雅。它会打印出一个可爱的报告(–spec),向你显示哪些测试用例通过/失败。花费30分钟学习它,你可以轻松地为模块创建基本测试。
  • Zombie-无头测试HTML和JavaScript的使用JSDom作为一个虚拟的“浏览器”。非常强大的东西。将其与Replay结合使用,可以对浏览器内的代码进行快速的确定性测试。

  • 关于如何“思考”测试的评论:

  • 测试不是可选的。有了这样的JavaScript动态语言,也有很少数的静态检查。例如,在执行代码之前,将两个参数传递给期望为4的方法不会中断。在JavaScript中创建错误的标准很低。基本测试对于弥补使用编译语言的验证差距至关重要。
  • 忘记验证,只需执行代码即可。对于每种方法,我的第一个验证案例是“一切都没有中断”,这是最常触发的案例。证明你的代码运行时不会抛出80%的错误,这将极大地提高你的代码置信度,你会发现自己回过头来并添加了略过的验证案例。
  • 从小处着手,打破惯性障碍。我们都很懒惰,时间紧迫,很容易将测试视为“额外工作”。所以从小开始。编写测试用例0-加载模块并报告成功。如果你强迫自己这样做,那么打破了惯性测试障碍。第一次(包括阅读文档)不到30分钟。现在编写测试用例1-调用你的方法之一,并验证“一切都没有中断”,也就是说,你没有收到错误。测试案例1应该花费你不到一分钟的时间。随着惯性的消失,逐渐扩大测试范围变得很容易。
  • 现在,用你的代码改进测试。不要被模拟服务器以及所有这些东西吓到了“正确的”端到端测试。代码从简单开始,不断发展以处理新情况。测试也应该如此。在为代码添加新案例和新复杂性时,请添加测试案例以执行新代码。当你发现错误时,添加验证和/或新案例以覆盖有缺陷的代码。当你正在调试并且对一段代码失去信心时,请返回并添加测试以证明它正在按照你的预期进行。捕获示例数据的字符串(从你调用的其他服务,你抓取的网站等),并将其提供给你的解析代码。这里有一些情况,那里的验证得到了改进,最终你将获得高度可靠的代码。
    另外,查看推荐的Node.js模块的官方列表。但是,GitHub的 Node Modules Wiki更完整,是一个很好的资源。

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

Node.js是基于事件的,并且是异步 / 非阻塞的。事件(例如传入的HTTP连接)将触发执行一些工作的JavaScript函数,并启动其他异步任务,例如连接到数据库或从另一台服务器提取内容。一旦启动了这些任务,事件功能就会完成,Node.js会重新进入睡眠状态。一旦发生其他情况,例如建立数据库连接或外部服务器响应内容,则回调函数将触发,并且将执行更多JavaScript代码,从而有可能启动更多异步任务(例如数据库查询)。这样,Node.js将愉快地交错多个并行工作流的活动,在任何时间点运行不受阻碍的任何活动。这就是为什么Node.js出色地管理数千个同时连接的原因。

为什么不像其他所有人那样仅对每个连接使用一个进程/线程?在Node.js中,新连接只是很小的堆分配。启动新进程会占用更多内存,在某些平台上为1 MB。但是实际成本是与上下文切换相关的开销。当你有10 ^ 6个内核线程时,内核必须做很多工作来弄清楚下一步应该执行谁。为Linux构建O(1)调度程序需要做大量工作,但最后,只有单个事件驱动的进程比争夺CPU时间的10 ^ 6进程更有效率。此外,在过载情况下,多进程模型的运行情况非常差,使关键的管理和管理服务(尤其是SSHD)饿死了(这意味着你甚至无法登录该框来了解它的实际运行情况)。

Node.js是单线程和无锁的。Node.js作为一个非常刻意的设计选择,每个进程只有一个线程。因此,根本上不可能有多个线程同时访问数据。因此,不需要锁。线程很难。真的很辛苦。如果你不相信这一点,则说明你没有完成足够的线程编程。正确地锁定是很难的,并且会导致很难发现的错误。消除锁和多线程可以使最讨厌的错误类别之一消失。这可能是节点的最大优势。

但是我如何利用我的16芯盒呢?

两种方式:

  1. 对于像图像编码这样的繁重的计算任务,Node.js可以启动子进程或将消息发送到其他工作进程。在此设计中,你将有一个线程管理事件流,并由N个进程执行繁重的计算任务,并消耗其他15个CPU。
  2. 为了扩展Web服务的吞吐量,你应该使用集群在一个盒子上运行多个Node.js服务器,每个内核运行一个(使用Node.js v0.6.x,此处链接的官方“集群”模块替换了具有不同的API)。然后,这些本地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");

现在,你可以单击一个URL并检查运行进程的状态。添加一些按钮,你将获得一个“管理门户”。如果你具有正在运行的Perl / Python / Ruby脚本,那么仅仅“扔进管理门户”并不十分简单。

但是JavaScript不是慢/不好/恶/恶魔衍生吗?JavaScript具有一些怪异的怪异之处,但是“好的部分”中存在一种非常强大的语言,无论如何,JavaScript是客户端(浏览器)上的THE语言。JavaScript将保留下来;其他语言则将其定位为IL,世界一流的人才正在竞争生产最先进的JavaScript引擎。由于JavaScript在浏览器中的作用,为了使JavaScript快速发展,人们投入了大量的工程工作。 V8是最新的,最强大的JavaScript引擎,至少在本月才是如此。它在效率和稳定性方面都吹灭了其他脚本语言(在你看来,Ruby)。而且只有在Microsoft,Google和Mozilla的庞大团队致力于解决这个问题,并争夺构建最佳JavaScript引擎的情况下,这种情况才会变得更好(它不再是JavaScript“解释器”,因为所有现代引擎都在处理大量的JIT)在幕后进行编译,仅将其解释为一次执行代码的备用。是的,我们都希望我们可以修复一些奇怪的JavaScript语言选择,但实际上还不错。而且该语言非常灵活,你实际上不是在编写JavaScript,而是在编写Step或jQuery-与其他任何一种语言相比,JavaScript在这些库中定义了体验。要构建Web应用程序,无论如何,你几乎都必须了解JavaScript,因此在服务器上进行编码具有某种技能组合的协同作用。它使我不必担心编写客户端代码。

此外,如果你真的讨厌JavaScript,则可以使用语法糖,例如CoffeeScript。或其他任何创建JavaScript代码的东西,例如Google Web Toolkit(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并不会困难得多,除了可以提高数千个并发连接的效率和可伸缩性之外,你还可以获得很多好处。 ..

2020-04-27