为什么选择Node作为我们的开发工具?

发表时间: 2016-12-30 11:06

为什么使用Node

假设你正在开发一个广告服务器,每分钟需要发布几百万条的广告。Node 的非阻塞I/O将是一个高效的解决方案,因为服务器能够最大限度地利用到所有的I/O 资源,而这一切不需要你写特殊的底层代码。并且,假如你已经有一支会写JavaScript 的开发团队,那么他们应该可以直接参与到Node 的项目中。传统的web 平台将无法做到这一点,这也是为什么像微软这样的公司也在积极地推动Node,尽管他们已经有了像.NET 那么优秀的平台。Visual Studio(.NET IDE) 的用户可以安装一些工具来支持对Node 的智能提示、性能监测,甚至npm。微软还开发了WebMatrix,它不但能直接支持Node,而且还能部署Node 项目。

Node 把非阻塞I/O 作为提高某些类型应用的性能的方式。JavaScript 传统的事件机制意味着在异步编程中,它有着相对方便以及容易理解的语法。在传统的编程语言中,I/O的操作将阻塞进程直到它完成为止。Node 的异步文件读写以及网络API 意味着在这些相对较慢的I/O 操作处理的时候主进程仍然能处理其他请求。下图展示了如何使用异步的网络和文件API 同时处理多个任务。

在图中,Node 的http 模块接收到并且解析了一个新的HTTP 请求① ,然后服务端的应用代码使用异步接口,将一个回调函数传入数据库的读取函数中来进行一次数据查询②。在等待数据返回的同时服务器能够从文件系统中读取网页模板文件③ ,这个模板文件被用来展示网页。一旦数据库完成查询,模板内容和数据库的返回数据将被用来渲染页面④。

在服务器处理这个请求的同时,服务器还可以用可用的资源处理其他的请求⑤ 。在不用考虑多线程的情况下开发这个广告服务,你可以仅使用最基本的JavaScript 编程技术,通过Node,非常高效地使用服务器I/O 资源。

其他Node 适用的场景是Web API 和网络爬虫,如果你需要下载以及截取网页的内容,那么Node 将是非常完美的解决方案,因为它能模拟DOM 操作,并且运行客户端JavaScript脚本。而且在这个场景中,Node 有着性能优势,因为网络爬虫主要的消耗在于网络和文件读写的I/O。

假如你需要调用或者开发一个JSON API,Node 也是一个非常棒的选择,因为它使得操作JavaScript 对象变得非常简单。Node 的一些web 框架(例如express)能够快速地搭建JSON API。

Node 不仅仅局限于web 应用,你可以创建任意你想要的TCP/IP 服务,比如网络游戏服务器,通过TCP/IP 套接字向各类玩家发送游戏状态,也可以在后台任务中维护游戏数据,将数据发送给玩家。

什么时候使用Node

下面是一些Node 适用的应用例子,来帮你像一个真正的Node 开发者一样来考虑这个问题。

情景:广告分布系统

Node 的强项:

• 有效地分配小块信息

• 处理潜在的网络速度慢的连接

• 容易扩展为多个处理器或服务器

情景:游戏服务器

Node 的强项:

• 使用JavaScript 来构建业务逻辑模型

• 不使用C 语言来开发迎合特定网络的服务器程序

情景:内容管理系统、博客

Node 的强项:

• 对已经有客户端JavaScript 开发经验的团队来说,可以很轻松地创建RESTful JSON APIs

• 轻量的服务器,和浏览器端JavaScript 结合

Node 的主要特性

Node 的主要特性是它的标准类库、模块系统以及npm(包管理系统),当然还有很多其他的。

实际上Node 最强大的特性是它的标准类库,它主要由二进制类库以及核心模块两部分组成,二进制类库包括libuv ,它为网络以及文件系统提供了快速的事件轮循以及非阻塞的I/O。同时它还有http 类库,所以你可以很快确定你的http 客户端与服务端。

上图是对Node 内部的高层次概述,展示了各个模块是如何组合的。

Node 的核心模块主要由JavaScript 编写,也就是说,假如你不理解或者你想了解更多细节,你可以直接阅读Node 的源码。这不但包括像网络、文件操作、模块系统,以及stream 这些模块,还包括Node 特有的特性,例如,通过cluster 模块同时运行多个Node进程,以及可以将代码片段封装在事件驱动的异常处理中的domain 模块。

接下来,我们将从事件开始深入每个核心模块。

1. EventEmitter 事件的接口

每个Node 开发者迟早会碰到EventEmitter ,一开始,它像是那些只有类库开发者才会使用的东西,但实际上它是大多数Node 核心模块的基础,Stream、网络、文件系统全部继承于它。

你可以基于EventEmitter 来创建自己基于事件的API,例如你要开发一个paypal 付款处理的模块,你可以让它基于事件,这样Payment 对象的实例可以触发像paid 和refund 这样的事件,通过这样的设计,你可以将这个模块从你的业务逻辑中分离出来,让它可以在其他项目中被重用。

一个有意思的地方是,stream 模块也是基于EventEmitter 的。

2. Stream:高可扩展性I/O 的基础

Streams 继承于EventEmitters ,能被用来在不可预测的输入下创建数据,比如网络连接,数据传输速度取决于其他用户正在干什么。通过Node 的stream API,你可以创建一个对象接收关于连接的事件,在接收到新数据时触发data 事件,在结束连接时触发end 事件,在有错误发生时触发error 事件。

相比较把许多的回调函数传入一个readable stream 的构造函数,你只订阅你关心的事件要好得多,多个streams 也可以连接起来,这样你可以用一个stream 对象从网络读取数据,把读取到的数据输送到另外一个stream 中加工成另外一个对象,可以把xml 文件的数据读取出来转换成JSON 格式,让JavaScript 操作起来更容易。

你可能觉得stream 和事件听上去很抽象,没错,它们的确很抽象,但它们是I/O 模块(例如文件系统和网络)的基础。

3. FS:处理文件

Node 的文件模块不但可以通过非阻塞的I/O 读写文件,而且它也有同步的方法。你可以通过fs.stat 异步获取文件的信息,也可以通过fs.statSync 同步读取。

如果你想通过Stream 的方式高效地处理文件内容,那么你可以通过fs.createReadStream来获得一个ReadableSream 对象。

4. 网络:创建网络客户端与服务端

网络模块是http 模块的基础,也可以用来创建通用的网络客户端与服务端。尽管Node开发通常指的是web 开发,在第7 章你会看到如何创建TCP 和UDP 的服务,这意味着你并不局限于http 开发。

5. 全局对象与其他模块

假如你有用Node 开发web 应用的经验,也许是Express 框架,那么你也许并不知道你已经使用了http 、net 以及fs 等核心模块。其他的内置模块也许不那么吸引眼球,但也是至关重要的。

全局对象与方法的设计就是其中一例,比如process 对象,它让你可以把数据传入或者传出标准I/O 流(stdout、stdin)。就像在UNIX 或者Windows 脚本中,你可以把数据通过cat 直接传给Node 程序。还有无处不见的console 对象,所有的JavaScript 开发都爱它,也是一个全局对象。

了解Node特性后,我们来看看《Node.js硬实战:115个核心技巧》一书的精简版目录。

第一部分Node 基础

1 入门

2 全局变量:Node 环境

3 Buffers:使用比特、字节以及编码

4 Events:玩转EventEmitter

5 流:最强大和最容易误解的功能

6 文件系统:通过异步和同步的方法处理文件

7 网络:Node 真正的“Hello, World”

8 子进程:利用Node 整合外部应用程序

第二部分实践中的技巧

9 网络:构建精简的网络应用

10 测试:编写健壮代码的关键

11 调试:用于发现和解决问题

12 生产环境中的Node:安全地部署应用程序

第三部分编写模块

13 编写模块,掌握Node 的所有

本文选自《Node.js硬实战:115个核心技巧》。