Node.js可以做什么? Node.js究竟是什么?

Node.js可以做什么? Node.js究竟是什么?

  
转载

一个 “编码就绪” 服务器

Node 是一个服务器端 JavaScript 解释器,它将改变服务器应该如何工作的概念。它的目标是帮助程序员构建高度可伸缩的应用程序,编写能够处理数万条同时连接到一个(只有一个)物理机的连接代码。

简介

如果您听说过 Node,或者阅读过一些文章,宣称 Node 是多么多么的棒,那么您可能会想:“Node 究竟是什么东西?” 即便是在参阅 Node 的主页之后,您甚至可能还是 不明白 Node 为何物?Node 肯定不适合每个程序员,但它可能是某些程序员一直苦苦追寻的东西。

为 试图解释什么是 Node.js,本文将简要介绍一些背景信息:它要解决的问题,它如何工作,如何运行一个简单应用程序,最后,Node 在什么情况下是一个好的解决方案。本文不涉及如何编写一个复杂的 Node 应用程序,也不是一份全面的 Node 教程。阅读本文应该有助于您决定是否应该继续学习 Node,以便将其用于您的业务。

Node 旨在解决什么问题?

Node 公开宣称的目标是 “旨在提供一种简单的构建可伸缩网络程序的方法”。当前的服务器程序有什么问题?我们来做个数学题。在 Java™ 和 PHP 这类语言中,每个连接都会生成一个新线程,每个新线程可能需要 2 MB 的配套内存。在一个拥有 8 GB RAM 的系统上,理论上最大的并发连接数量是 4,000 个用户。随着您的客户群的增长,如果希望您的 Web 应用程序支持更多用户,那么,您必须添加更多服务器。当然,这会增加服务器成本、流量成本和人工成本等成本。除这些成本上升外,还有一个潜在技术问题,即 用户可能针对每个请求使用不同的服务器,因此,任何共享资源都必须在所有服务器之间共享。鉴于上述所有原因,整个 Web 应用程序架构(包括流量、处理器速度和内存速度)中的瓶颈是:服务器能够处理的并发连接的最大数量。

Node 解决这个问题的方法是:更改连接到服务器的方式。每个连接发射一个在 Node 引擎的进程中运行的事件,而不是为每个连接生成一个新的 OS 线程(并为其分配一些配套内存)。Node 声称它绝不会死锁,因为它根本不允许使用锁,它不会直接阻塞 I/O 调用。Node 还宣称,运行它的服务器能支持数万个并发连接。

现在您有了一个能处理数万个并发连接的程序,那么您能通过 Node 实际构建什么呢?如果您有一个 Web 应用程序需要处理这么多连接,那将是一件很 “恐怖” 的事!那是一种 “如果您有这个问题,那么它根本不是问题” 的问题。在回答上面的问题之前,我们先看看 Node 的工作原理以及它的设计运行方式。

Node 肯定不是什么?

没错,Node 是一个服务器程序。但是,基础 Node 产品肯定 像 Apache 或 Tomcat。本质上,那些服务器 “安装就绪型” 服 务器产品,支持立即部署应用程序。通过这些产品,您可以在一分钟内启动并运行一个服务器。Node 肯定不是这种产品。Apache 能通过添加一个 PHP 模块来允许开发人员创建动态 Web 页,添加一个 SSL 模块来实现安全连接,与此类似,Node 也有模块概念,允许向 Node 内核添加模块。实际上,可供选择的用于 Node 的模块有数百个之多,社区在创建、发布和更新模块方面非常活跃,一天甚至可以处理数十个模块。本文后面将讨论 Node 的整个模块部分。

Node 如何工作?

Node 本身运行 V8 JavaScript。等等,服务器上的 JavaScript?没错,您没有看错。对于只在客户机上使用 JavaScript 的程序员而言,服务器端 JavaScript 可能是一个新概念,但这个概念本身并非遥不可及,因此为何不能在服务器上使用客户机上使用的编程语言?

什么是 V8?V8 JavaScript 引擎是 Google 用于其 Chrome 浏览器的底层 JavaScript 引擎。很少有人考虑 JavaScript 在客户机上实际做了些什么?实际上,JavaScript 引擎负责解释并执行代码。Google 使用 V8 创建了一个用 C++ 编写的超快解释器,该解释器拥有另一个独特特征;您可以下载该引擎并将其嵌入任何 应用程序。V8 JavaScript 引擎并不仅限于在一个浏览器中运行。因此,Node 实际上会使用 Google 编写的 V8 JavaScript 引擎,并将其重建为可在服务器上使用。太完美了!既然已经有一个不错的解决方案可用,为何还要创建一种新语言呢?

事件驱动编程

许多程序员接受的教育使他们认为,面向对象编程是完美的编程设计,这使得他们对其他编程方法不屑一顾。Node 使用了一个所谓的事件驱动编程模型。

清单 1. 客户端上使用 jQuery 的事件驱动编程
// jQuery code on the client-side showing how Event-Driven programming works // When a button is pressed, an Event occurs - deal with it // directly right here in an anonymous function, where all the // necessary variables are present and can be referenced directly $("#myButton").click(function(){ if ($("#myTextField").val() != $(this).val()) alert("Field must match button text"); });

实际上,服务器端和客户端没有任何区别。没错,这没有按钮点击操作,也没有向文本字段键入的操作,但在一个更高的层面上,事件正在 发生。一个连接被建立,这是一个事件!数据通过连接进行接收,这也是一个事件!数据通过连接停止,这还是一个事件!

为 什么这种设置类型对 Node 很理想?JavaScript 是一种很棒的事件驱动编程语言,因为它允许使用匿名函数和闭包,更重要的是,任何写过代码的人都熟悉它的语法。事件发生时调用的回调函数可以在捕获事件处 进行编写。这样可以使代码容易编写和维护,没有复杂的面向对象框架,没有接口,没有过度设计的可能性。只需监听事件,编写一个回调函数,其他事情都可以交 给系统处理!

示例 Node 应用程序

最 后,我们来看一些代码!让我们将讨论过的所有内容汇总起来,从而创建我们的第一个 Node 应用程序。我们已经知道,Node 对于处理高流量应用程序很理想,所以我们将创建一个非常简单的 Web 应用程序,一个为实现最快速度而构建的应用程序。下面是 “老板” 交代的关于我们的样例应用程序的具体要求:创建一个随机数字生成器 RESTful API。这个应用程序应该接受一个输入:一个名为 “number” 的参数。然后,应用程序返回一个介于 0 和该参数之间的随机数字,并将生成的数字返回给调用者。由于 “老板” 希望该应用程序成为一个广泛流行的应用程序,因此它应该能处理 50,000 个并发用户。我们来看看以下代码:

清单 2. Node 随机数字生成器
/ these modules need to be imported in order to use them. // Node has several modules. They are like any #include // or import statement in other languages var http = require("http"); var url = require("url"); // The most important line in any Node file. This function // does the actual process of creating the server. Technically, // Node tells the underlying operating system that whenever a // connection is made, this particular callback function should be // executed. Since we're creating a web service with REST API, // we want an HTTP server, which requires the http variable // we created in the lines above. // Finally, you can see that the callback method receives a 'request' // and 'response' object automatically. This should be familiar // to any PHP or Java programmer. http.createServer(function(request, response) { // The response needs to handle all the headers, and the return codes // These types of things are handled automatically in server programs // like Apache and Tomcat, but Node requires everything to be done yourself response.writeHead(200, {"Content-Type": "text/plain"}); // Here is some unique-looking code. This is how Node retrives // parameters passed in from client requests. The url module // handles all these functions. The parse function // deconstructs the URL, and places the query key-values in the // query object. We can find the value for the "number" key // by referencing it directly - the beauty of JavaScript. var params = url.parse(request.url, true).query; var input = params.number; // These are the generic JavaScript methods that will create // our random number that gets passed back to the caller var numInput = new Number(input); var numOutput = new Number(Math.random() * numInput).toFixed(0); // Write the random number to response response.write(numOutput); // Node requires us to explicitly end this connection. This is because // Node allows you to keep a connection open and pass data back and forth, // though that advanced topic isn't discussed in this article. response.end(); // When we create the server, we have to explicitly connect the HTTP server to // a port. Standard HTTP port is 80, so we'll connect it to that one. }).listen(80); // Output a String to the console once the server starts up, letting us know everything // starts up correctly console.log("Random Number Generator Running...");

启动应用程序

将上面的代码放入一个名为 “random.js” 的文件中。现在,要启动这个应用程序并运行它(以便创建 HTTP 服务器并监听端口 80 上的连接),只需在您的命令提示中输入以下命令:% node random.js。下面是服务器已经启动并运行时看起来的样子:

root@ubuntu:/home/moila/ws/mike# node random.js Random Number Generator Running...

访问应用程序

应 用程序已经启动并运行。Node 正在监听所有连接,我们来测试一下。由于我们创建了一个简单的 RESTful API,所以可以使用 Web 浏览器来访问这个应用程序。键入以下地址(确保您已完成了上面的步骤):http://localhost/?number=27。

您的浏览器窗口将更改到一个介于 0 到 27 之间的随机数字。单击浏览器上的 “重新载入” 按钮,您会得到另一个随机数字。就是这样,这就是您的第一个 Node 应用程序!

Node 对什么有好处?

到此为止,您可能能够回答 “Node 是什么” 这个问题了,但您可能还有一个问题:“Node 有什么用途?” 这是一个需要提出的重要问题,因为肯定有些东西能受益于 Node。

它对什么有好处?

正如您此前所看到的,Node 非常适合以下情况:在响应客户端之前,您预计可能有很高的流量,但所需的服务器端逻辑和处理不一定很多。Node 表现出众的典型示例包括:

  • RESTful API

    提 供 RESTful API 的 Web 服务接收几个参数,解析它们,组合一个响应,并返回一个响应(通常是较少的文本)给用户。这是适合 Node 的理想情况,因为您可以构建它来处理数万条连接。它仍然不需要大量逻辑;它本质上只是从某个数据库中查找一些值并将它们组成一个响应。由于响应是少量文 本,入站请求也是少量的文本,因此流量不高,一台机器甚至也可以处理最繁忙的公司的 API 需求。

  • Twitter 队列

    想 像一下像 Twitter 这样的公司,它必须接收 tweets 并将其写入数据库。实际上,每秒几乎有数千条 tweet 达到,数据库不可能及时处理高峰时段所需的写入数量。Node 成为这个问题的解决方案的重要一环。如您所见,Node 能处理数万条入站 tweet。它能快速而又轻松地将它们写入一个内存排队机制(例如 memcached),另一个单独进程可以从那里将它们写入数据库。Node 在这里的角色是迅速收集 tweet,并将这个信息传递给另一个负责写入的进程。想象一下另一种设计(常规 PHP 服务器会自己尝试处理对数据库本身的写入):每个 tweet 都会在写入数据库时导致一个短暂的延迟,因为数据库调用正在阻塞通道。由于数据库延迟,一台这样设计的机器每秒可能只能处理 2000 条入站 tweet。每秒处理 100 万条 tweet 则需要 500 个服务器。相反,Node 能处理每个连接而不会阻塞通道,从而能够捕获尽可能多的 tweets。一个能处理 50,000 条 tweet 的 Node 机器仅需 20 台服务器即可。

  • 电子游戏统计数据

    如 果您在线玩过《使命召唤》这款游戏,当您查看游戏统计数据时,就会立即意识到一个问题:要生成那种级别的统计数据,必须跟踪海量信息。这样,如果有数百万 玩家同时在线玩游戏,而且他们处于游戏中的不同位置,那么很快就会生成海量信息。Node 是这种场景的一种很好的解决方案,因为它能采集游戏生成的数据,对数据进行最少的合并,然后对数据进行排队,以便将它们写入数据库。使用整个服务器来跟踪 玩家在游戏中发射了多少子弹看起来很愚蠢,如果您使用 Apache 这样的服务器,可能会 有一些有用的限制;但相反,如果您专门使用一个服务器来跟踪一个游戏的所有统计数据,就像使用运行 Node 的服务器所做的那样,那看起来似乎是一种明智之举。

Node 模块

尽 管不是本文最初计划讨论的主题,但应广大读者要求,本文已经扩展为包含一个 Node Modules 和 Node Package Manager 简介。正如已经习惯使用 Apache 的开发人员那样,您也可以通过安装模块来扩展 Node 的功能。但是,可用于 Node 的模块极大地 增强了这个产品,那些模块非常有用,将使用 Node 的开发人员通常会安装几个模块。因此,模块也就变得越来越重要,甚至成为整个产品的一个关键部分。

在 “参考资料” 部分,我提供了一个指向模块页面的链接,该页面列示了所有可用模块。为了展示模块能够提供的可能性,我在数十个可用模块中包含了以下几个模块:一个用于编 写动态创建的页面(比如 PHP),一个用于简化 MySQL 使用,一个用于帮助使用 WebSockets,还有一个用来协助文本和参数解析的模块。我不会详细介绍这些模块,这是因为这篇概述文章旨在帮助您了解 Node 并确定是否需要深入学习(再次重申),如果需要,那么您肯定有机会用到这些可用模块。

另外,Node 的一个特性是 Node Package Module,这是一个内置功能,用于安装和管理 Node 模块。它自动处理依赖项,因此您可以确定:您想要安装的任何模块都将正确安装并包含必要的依赖项。它还支持将您自己的模块发布到 Node 社区,假如您选择加入社区并编写自己的模块的话。您可以将 NPM 视为一种允许轻松扩展 Node 功能的方法,不必担心这会破坏您的 Node 安装。同样,如果您选择深入学习 Node,那么 NPM 将是您的 Node 解决方案的一个重要组成部分。

结束语

阅 读本文之后,您在本文开头遇到的问题 “Node.js 究竟是什么东西?” 应该已经得到了解答,您应该能通过几个清晰简洁的句子回答这个问题。如果这样,那么您已经走到了许多程序员的前面。我和许多人都谈论过 Node,但他们对 Node 究竟用于做什么一直很迷惑。可以理解,他们具有的是 Apache 的思维方式,认为服务器就是一个应用程序,将 HTML 文件放入其中,一切就会正常运转。由于大多数程序员都熟悉 Apache 及其用途,因此,描述 Node 的最简单方法就是将它与 Apache 进行比较。Node 是一个程序,能够完成 Apache 能够完成的所有任务(借助一些模块),而且,作为一个可以将其作为基础进行构建的可扩展 JavaScript 平台,Node 还能完成更多的任务。

从 本文可以看出,Node 完成了它提供高度可伸缩服务器的目标。它使用了 Google 的一个非常快速的 JavaScript 引擎,即 V8 引擎。它使用一个事件驱动设计来保持代码最小且易于阅读。所有这些因素促成了 Node 的理想目标,即编写一个高度可伸缩的解决方案变得比较容易。

与理解 Node  什么同样重要的是,理解它不是 什么。Node 并不只是 Apache 的一个替代品,它旨在使 PHP Web 应用程序更容易伸缩。事实远非如此。尽管 Node 还处于初始阶段,但它发展得非常迅速,社区参与度非常高,社区成员创建了大量优秀模块,一年之内,这个不断发展的产品就有可能出现在您的企业中。


本文转载自:http://www.ibm.com/developerworks/cn/opensource/os-nodejs/index.html?ca=drs-

来源:http://www.oschina.net/question/91955_47352





Node.js真的无所不能?那些不适用的应用领域分析

Node.js是一个服务器端JavaScript解释器,底层采用的还是libevent;它的目标是帮助程序员构建高度可伸缩的应用程序,目前 对Node.js 的采用状况,Node.js 官方站点有一些罗列,但是相当不完整。如果你自己公司用到,也可以在 github 上提交自己的 pull-request 来更新这个文档。

http://nodejs.org/industry/

https://github.com/joyent/node/wiki/Projects,-Applications,-and-Companies-Using-Node

其实到今天为止,很少有哪些大的互联网公司是和 Node.js 无关的。LinkedIn,Yahho,Paypal, eBay, Walmart 都在将既有的系统向 Node.js 迁移(https://www.quora.com/Node-js/What-companies-are-using-Node-js-in- production 看)。国内的淘宝、网易、百度等也都有很多项目运行在 Node.js 之上。

2011 年我开始接触 Node.js 的时候,npmjs.org 上只有不到 3,000 个 Node.js 的 packages,今天(2014-3-2)则有 61,897 个,这个数字还在快速增长中。

下面有两个链接,第一个是在讲 Walmart 这几年为什么以及如何迁移到 Node.js 上;第二个则为 eBay 是如何从 Node.js 的怀疑者转变为采用者。

  • Node.js at Walmart
  • eBay’s Node.js Adoption Journey

《Announcing ql.io》 这篇文章的最后一段,列出了 eBay 为什么选择 Node.js。

每天都有几百个新的 packages 被发布到 npm 上,小到几行代码,大到万行代码的 Framework。一天有7百万次的包下载(安装到某台电脑上),对于单一开发框架的社区来说,用沸腾的海洋来形容并不过分。
以下应用领域和程序员不适合选择Node.js:

  • 计算密集型应用。Javascript 的计算性能是很难和 C 语言代码相比的。当然,也有反例:http://onlinevillage.blogspot.jp/2011/03/is-javascript- is-faster-than-c.html,只不过不具有典型性。
  • 需要精密控制内存的分配和释放的场景,如果用 Node.js 实现 Redis 数据库,虽然程序会简单不少,但是 JVM 对内存数据结构的精密控制能力是比不了用 C 语言纯手工打造的。
  • 大量且需要频繁通过 C Binding 调用 C library 的情况。这种场景下,往返参数的 Marshal/Unmarshal 的成本可能会大于 C Library 带来的性能提升。
  • 实时性要求很高的场景,例如:交换机或者工控机器人。这是因为所有通过垃圾回收机制来管理内存的系统都有可能在 GC 过程中产生停顿,从而影响响应速度,而且很难优化。
  • 需要单一进程控制大内存的场景:v8 引擎的设计限制,在 32-bit下有 1GB 最大堆尺寸的限制,在 64-bit下是1.7GB。当然,由于 node.js buffer 的分配不是在 v8 的堆上,因此可以超过此限制。这个限制可以通过向 v8 引擎传递max_old_space_size 参数来超越,但是也会带来 GC 的性能退化。这一问题在几乎所有 GC Based 的系统下都存在。
  • 不关心系统吞吐率或者不需要异步调用的场景:例如,自动化脚本,这些脚本不需要关心多用户并发访问的性能消耗。用 Python 这样的“胶水”语言写起来会更简单。
  • 某些非通用场景:例如 nginx 对于静态 web server 或者 反向代理的场景是特别设计的,这些场景中 nginx 的性能比 Node.js 要好。
  • 强类型强迫症:有些 Java 或者 .NET 过来的程序员会认为只有强类型语言和严格定义的类型系统是专业化的象征,构造这样的系统是架构师的使命,而动态语言只是玩具,只能用来做 Demo 或者前端开发。
  • 团队成员难以理解或者接受函数式编程:Javascript 本质上更像函数式语言,有些程序员在理解和使用闭包、高阶函数等概念时总是不能习惯,这个问题在国内的开发团队中还挺普遍的。
  • 回调式编程的不习惯:Node.js 的异步IO 大量依赖回调。回调让程序的执行出现了两条路径,出现故障时调用栈也很难理解。这对习惯了同步编程的程序员来说一开始确实是个坎。async, Q promise 等 package 可以缓解这个问题(在 ES6 的Generator 普及之前),不过这也带来了更陡峭的学习曲线。一般情况下,需要半年到一年的习惯过程,当然前提是多看,多写。随着越来越多的经验分享,这个过程也在不断 地缩短。

除此之外的领域,或者没有上述问题的,都都可以享受到 Node.js 带来的生产力提升和稳定的性能保障。

性能的争议

不同开发环境间的性能对比从来都是有争议的话题。我只能说,当开发 Web 或 网络环境下的应用时,Node.js 靠以下几个方面来避免出现不必要的性能问题:

  1. Chrome V8,一个可靠的优秀的虚拟机(hidden classes 和 inline caching),让 Javascript 的运行速度进入了第一阵营(C++, Java, .NET)。
  2. 异步 IO 大大降低了线程数量,莫名其妙的死锁和等待的概率被降低了很多。大部分场景不用去考虑并发和同步锁,犯错误的机会少。而在 Python 中,异步 IO 并不是标准,并没有被贯穿到所有 Package 中,因此应用程序也就很难获得一致的性能保障。
  3. 非常轻巧的“内核”。Node.js 的模块分为 Core Modules 和 Userland 两部分。Core Modules 非常精简,只包括 TCP, HTTP, DNS, File System, child processes 和其他一些模块,这些网络库还只有异步版本。相对地,在 Userland 中却有着海量的 Packages。开发应用的时候,我们根据应用的需求来组合 Userland 的 Packages,使得我们的应用程序有机会在一个很低的资源消耗水平下运行(在《Announcing ql.io》中 指出,一台开发服务器就可以支持 12 万活跃连接,平均每个连接消耗 2k 内存)。事实上,我开发的 WebSockte 应用在 Raspberry Pi 下都可以支持几百并发长连接(WebSocket)。和那些动辄上万个类的企业开发框架相比,这是一个巨大的优势。这种方式降低了出现问题的概率、查找问 题的成本以及减少部署成本。

对 Javascript 的绝对性能的追求一直没有停顿(例如, Mozilla 的 asm.js )。而 Node.js 则在绝对性能的基础上,确保应用程序可以获得稳定和可预测的性能保障( Benchmark 和实际的应用运行往往是两回事)。

Node.js继承了JavaScript 的灵活性,优秀的JS库应当如何选择

可以在 npmjs.org 或者 google 上搜索关键词。如果类似的返回很多,则看其被其他 package 依赖的数量有多少。上 github 上查看 starred 和 forks 的数量,读 issues。

如果是“名人”(substack, visionmedia (TJ Holowaychuk), dominictarr, rvagg 等)写的 Packages 自然会被加分。

最后是把 Git Repo. Clone 或者 Fork 下来, 阅读且注释他们的源代码。这个过程也可以发现很多他们依赖的其他 Packages。这是一个蛮享受的过程,可以学到很多新知识和新的用法。

还有一些乱枪打鸟的方法:

  1. 在 Tweeter 上关注 @nodenpm,所有在 npm 上发布或者更新的 packages 都会在该 handle 上发布出来。在你的碎片时间没事可以刷刷这个,当然你需要 APN 。
  2. 关注一些推荐和评论账号:@dailyjs,@echojs 等。
  3. Changelog 会提供不错的开源信息汇总,其中包括 Node.js、Javascript 和 npm 栏目。
  4. Hacker News 则不会让你忽略软件行业的一些“大事”或者新概念。

一个项目开始前的研究阶段,我大约会浏览几十个 Packages,精读其中的5 ~ 10个。开发过程中则根据需要还会不断地发现和精读一些,这些都被我计入了项目的成本。

“自由选择,自己负责”,在这个庞大的开发社区了不要指望有人能告诉你“标准答案”。每个人面临的问题域和知识背景都不一样,坚持多看,多试,多思考,享受获得新知识的过程比获得“标准答案”更重要。

在众多的成熟开发框架下为什么需要Node.js

在每一个特定的问题域,大家总是在尝试找到最优解。这个过程是没有终结的,就想最终也会有其他框架代替 Node.js 一样。

今天的 Web,是无数相互连接的 Web Services 组成的,这些连接的本质是异步的。Node.js 天生异步的特性和这个场景的匹配度相对其他开发框架要更高,因此实现起来也更自然。

除此之外,Node.js 的设计基本原则遵循了 《Unix 的编程艺术》,参见 Isaac Z. Schlueter (前任 Node.js 的Gatekeeper,目前负责 npm 的商业化) 的Blog: Unix Philosophy and Node.js。

npm 和 stream 就是上述哲学的产物。

npm

npm 是 Node.js 的包管理系统。包管理系统不是新东西,但是和 npm 的那些前辈和表兄弟不同的是:

  1. npm 直接集成在 Node.js 中,无需单独安装,发布,安装 packages 非常简单。
  2. npmjs.org 提供一个统一的入口,你可以看到每个 package 被哪些 packages 所依赖,你也可以一目了然地看到它依赖了誰,以及最近的下载次数。结合到 github 上的更新情况,基本上对一个 package 的基本情况你都能了解到。
  3. 约定俗成的发布规范:一个 git repo. 让你可以直接找到源代码;README.md 提供简要的说明让消费者能尽快用起来。

对于开发者来说,每一个 package 就是一个 “micro service”,是最小重用单元。大部分的 package 只有几百行代码,甚至有些只有几行代码。这样的重用粒度是在其他社区难以想象的。

在 Node.js 的应用的开发过程中,编写 “一口尺寸”(bite-size)的 module 是推荐的编程方式。这也很方便你把这些小 module 封装为 package 分享到社区当中,而不用担心泄露“企业机密”。

npm 是每一个 Noder 的 “home”,也是每一个 Node.js 应用的系统架构的一部分。

Stream

如果说,npm 提供了“开发时重用”的机制,那么 stream 的则提供了“运行时”不同组件之间的“重用”机制。stream 概念和 unix 中的 stream 对应,应用中的每一个 component 则对应 unix 的 filter。下面举一个实际的例子:

在某个应用中,我需要一个 API Server,它的客户端包括 Web Browser,iOS App., 以及网络中的其他 Server。Web Browser 和我们的 API Server 的通信基于 SockJS(当 然你也可以选择 SocketIO,或者 Faye 等),它为浏览器兼容提供了适当的 “Fallback” 方案;对于 iOS App.来说,由于不需要考虑浏览器兼容,则采用基于标准的 RFC 6455 的纯 WebSocket 通信协议,这样实现起来更简单;而对于其他 Server 来说,局域网内则用 TCP,互联网上则用 TLS 来保证传输安全。

我在 Node.js 上是这么实现的:

  • 利用 donde 构建一个通信无关的 RPC Server 来提供 API 服务。
  • 用 Node.js Core Modules 中的 tcp, tls 创建 TCP/TLS Server 并监听,用第三方的 SockJS 和 websocket-stream 分别创建 SockJS 和 WebSocket 的 Server 并监听。
  • 当 Client 连接到不同的端口,在 Server 上就会创建基于该协议的 Commnucation Stream,然后创建一个新的 dnode 实例,得到一个 dnode 的 Stream。最后将 Commnucation Stream 和 dnode Stream 像接水管一样接到一起即可。

考虑到在不稳定的网络环境下的自动重连需求,也可以添加 reconnect。

不算你自己 RPC API 的实现逻辑,支持这么多的通信协议的 Server 框架只需要百十行代码,还加上了一定程度的异常处理。

tcp, tls, SockJS,或者 reconnect 的开发者并不能确定“消费者”是如何使用这些 Package 的,但是大家都支持 Stream 的接口,则让自己的 Package 能够被运用到更多的场景。

进一步,我们也可以多路复用一个底层的 Stream。我们把上面的例子再扩展一下:

在既有的通信连接上(connStream),除了提供 RPC API 之外,还需要添加分布式的状态同步功能,例如:通过 Scuttlebutt,完成 Client 与 Server 或 Server 与 Server 之间的常量数据自动同步,而不用为这些功能设计新的 RPC API。通过 mux-demux ,可以复用既有的网络通信 Stream(tcp, SockJS, Web Socket…),避免建立不必要的网络连接。

Stream 是 Node.js 的核心概念之一,其接口和工作方式被广泛地采用,为不同组件在运行时相互通信提供了最基本的支持。

在 Node.js 中,如何使用 Stream 可以用一本书的容量来描述,不是因为 Stream 的概念有多复杂,而是因为其组合方式非常丰富。

小结

三年前接触 Node.js,并且学习和采用,主要原因是因为 Node.js 在解决当今网络应用的问题时,提供了高性能、高可靠和低功耗的方法。高性能、高可靠和低功耗,不是在于 Node.js 做了什么,而是在于 Node.js 不做什么。Node.js 和 Javascript 的概念,在 Java 或者其他开发框架中都能找到对应的概念。但是 Node.js 仅保留了它认为最重要的部分作为 Core Modules,其他都让给了 User Land,这才是高性能、高可靠和低功耗的最本质的保障。

随着 Node.js 这三年的发展,今天使我浸淫其中的理由已经不是之前的那些特点了。

npm 建立了一个“人人为我,我为人人”社区,无论你是一个入门级的 Noder,还是一个多年的老兵,都在自觉或不自觉地从这个社区吸取营养,也在不断地回馈社区。在使用 npm 的过程中,你会很自然地发现,将自己的应用切割为尽量小的 Modules,发布为公有的 Packages,配上一个简单扼要的 README.md,反而是最有效率的系统架构方式。

Node.js 所遵循的 Unix 设计哲学,又提供了最简单有效的复用规范。简单有效,才会被大家自觉采用,采用得越多,重用的可能性就更大。以Express 4.0 ( MEAN 架构中的那个 ‘E’ )为例,这么一个流行的 MVC Web Framework的核心代码只有 2,600 多行(不算测试,中间件和例子,但是包括注释)。

npm 和 github 一起,为今天的软件生产提供了新的生产关系,这也是当前 Node.js 超越其他社区的根本原因。不是单纯的性能,也不仅仅是因为动态语言,甚至不是因为大量熟悉 Javascript 的前端程序员(和后端程序员相比,由于缺少系统性的思维,前端 Javascript 程序员掌握 Node.js 未必有多少优势 ),而是以更加便捷的分享式开发为基础的生产关系实实在在地提升了软件生产力。


Node.js的驾驭能力

如果“复杂的后端程序” 等于 “庞大的继承树”,“强类型安全”,“精细的异常定义和处理”,那么 Node.js 当然无法驾驭。因为 Node.js 和 Java, .NET 相比,是一颗独立的“科技树”。原型继承、函数式编程、模块系统、回调…,这些概念和编程方式对习惯了 Java 以及 .NET 的程序员来说不仅仅是不熟悉,甚至一开始会产生“不舒服的”感觉。

从我的体验来看(Basic->C->VB->Delphi->.NET->Node.js),这种不舒服更多地来 自于之前对严谨的类型系统的信仰。原本所谓的“架构师”,承担着整个应用或项目的类型系统的建设任务,对任何破坏类型一致性的行为都会自然而然的产生抵触 情绪。要想掌握 Node.js,最好的方法是先从 Java, .NET 这颗“科技树”上爬下来,清空自己,然后重新爬 Node.js 这棵树。

程序员从 Java, .NET 可以学到面向对象和泛型这些重用手段,而在 Node.js 的世界中,当你接触到大量来自于完全不同背景的程序员所编写的 Packages 的时候,你也会意识到,不是每样东西都是“类”,重用也不一定都基于继承。虽然有人试图在 Node.js 中克隆之前自己熟悉的类型系统,但是更多的程序员则在不断尝试更优雅、简单的编写方式。在 Node.js 的开发过程中,没有所谓的“最佳实践”,类似的问题总会有人尝试不同的解决方法。对于一个勤于思考和反思的程序员,这是一个充满乐趣的过程。反之,如果你 的团队是由缺少独立思考或者独立解决问题的程序员组成的,那么 Node.js 确实不适合。你需要用强类型语言搭好一个受限的框架,然后让体力型的队友去填空。

我们公司只有两个程序员,一个负责 iOS 开发,而我负责“复杂”后端程序和 Web Browser 开发。如果用 Java 或者 .NET 来开发,完成同样的功能需要至少三倍以上的人力。

Node.js能否统一前后端

完全统一既不可能,也没必要。再说这个所谓的“统一”与其放到 Node.js 脑袋上,不如送给 Javascript,因为 Javascript 用到的场景太多了。让我们看几个事实:

  1. Node.js 让我们可以用 Javascript 写后台程序
  2. Node.js 让我们写 Web Browser 前端:一方面,可以通过 Grunt 或者其他持续集成工具生成可发布的前端静态网站内容(例如: Bootstrap); 另一方面,也可以通过 Browserify 在前端代码中使用 Node.js 的 Modules,让前后台代码使用统一的代码基(例如:domready。很多 Node.js 的 Modules 本身就经过了浏览器兼容测试,可以同时运行在两端)。
  3. 用 Node.js 开发桌面应用,例如:https://github.com/rogerwang/node-webkit/wiki/List-of-apps- and-companies-using-node-webkit,列出了基于 node-webkit 的桌面应用列表。
  4. 即使在性能受限的移动设备中,我们也可以通过 Javascript Binding 将一部分应用逻辑用 Javascript 来实现,而 UI 的渲染还是 Native 的方式。这在很多游戏中已经被采用(http://www.zhihu.com/question/21130385)。只要用到 Javascript,或者说,随着 Javascript 代码基的扩大,npm based 的包管理方式就会通过 Browserify 的方式被慢慢引入。

除了第二个问题提到的那些不适合 Node.js 的地方,其他领域想彻底不碰 Node.js 是很难的。


Node.js发展方向

如果你的老板不让你碰 Node.js,你需要让他支付青春损失费。开个玩笑:)

投资在 Node.js 不会吃亏。

基于 Browserify 的贡献,前后台一致的代码基正在成为现实(在我的一个项目中已经如此,Web Client 通过 Node.js 的 stream 和后端传递数据 )。你可以看到在前端使用原本为后台写的Module,或者用写后端程序的方法写前端代码,例如:domready。浏览器中无需运行一个完整的 Node.js,只要打包好需要的 Modules 下载到浏览器执行即可。

在问题 5 中,大家已经看到 Node.js 在分布式计算领域的应用能力。 在问题 8 中,大家可以看到 Node.js 在客户端开发中所扮演的角色。

传统的数据库这一领域也在发生变化。通用的数据库系统在未来会慢慢“失宠”,“乐高积木”化的存储服务会流行起来。Hackers 们围绕着 LevelUp构 建自己的存储引擎,从 key/value,到Graph DB;从基于 B 树的一维索引到基于 R-Tree 的多维索引;从能够在浏览器中运行的嵌入数据库到支持成千上万访问者,高可用的数据库系统;从支持两阶段提交的 Transaction 到,到支持实时增量的 Map-Reduce。在我的一个项目中已经开始采用这种方法,为特定的存储和查询需求构建特定的存储服务。这在以前是不可想象的,但是现在,也就是一个 程序员的工作吧。

我是一个自己写程序的“产品经理”。每年有8个月是集中开发的时间,剩下的时间则是负责产品设计。关注的方向从 Web、Mobile App. 到后端系统。Node.js 给我提供了无数块“乐高积木”,让我可以拼装自己的玩具,这是很快乐的体验的过程。遗憾的就是时间不够多,有那么多东西没时间去了解,去体会其他人的奇思 妙想。小公司也是对成本极度敏感的,如果没有 Node.js 很多东西连想都不敢想。

你可能感兴趣的:(Node.js可以做什么? Node.js究竟是什么?)