【迷你书】深入浅出Node.js


内 容 提 要 本书从不同的视角介绍了 Node 内在的特点和结构。由首章 Node 介绍为索引,涉及 Node 的各个方面, 主要内容包含模块机制的揭示、异步 I/O 实现原理的展现、异步编程的探讨、内存控制的介绍、二进制数 据 Buffer 的细节、Node 中的网络编程基础、Node 中的 Web 开发、进程间的消息传递、Node 测试以及通过 Node 构建产品需要的注意事项。最后的附录介绍了 Node 的安装、调试、编码规范和 NPM 仓库等事宜。 本书适合想深入了解 Node 的人员阅读。 定价:69.00元 读者服务热线:(010)51095186转600 印装质量热线:(010)81055316 反盗版热线:(010)81055315 广告经营许可证:京崇工商广字第 0021 号 编  著 朴 灵 责任编辑 王军花 执行编辑 董苗苗 责任印制 焦志炜 人民邮电出版社出版发行  北京市丰台区成寿寺路11号 邮编 100164  电子邮件 315@ptpress.com.cn 网址 http://www.ptpress.com.cn 北京      印刷 开本:800×1000 1/16 印张:21.75 字数:514千字 2013年 12 月第 1 版 印数:1 — 4 000册 2013年 12 月北京第 1 次印刷 ◆ ◆ ◆ 错误!文档中没有指定样式的文字。 1 1 2 3 5 7 10 12 8 9 4 6 11 序一 没有用过 Node 的人,是不会相信仅凭 JavaScript 这门活跃于网页编程的脚本语言就可以驱 动后端复杂的应用程序,也不会相信 Node 在开发高并发、高性能后端服务程序上也有着极大的 优势。 我们在 2010 年接触 Node 的时候,国内外了解 Node 的人寥寥可数,2011 年我们已经决定在 淘宝的部分生产系统中开始使用 Node。由于招募熟悉 Node 的人才是个大问题,为了树立技术品 牌,我们在 2011 年年初创办 CNode 开源技术社区(CNodeJS.org),没有想到一发不可收拾。从 2011 年 4 月开始,我们走遍北京、上海、广州、深圳、杭州,甚至还到了香港,发起并且组织了 多次 NodeParty 线下技术分享。为了弥补初学者没有 Node 托管环境学习测试的问题,我们还自 己研发了 Node App Engine。Node 在国内深入人心,我相信与 CNode 社区有着不小的关系。 最初,Node 的爱好者大都是些喜欢探索新技术的极客。在社区,我们也认识了很多天南海 北的朋友,包括朴灵。在一次上海 Node 技术分享会后,我邀请他加入了淘宝。他在淘宝工作之 余继续为社区作贡献,自发为 Node 的推广做了很多事情,包括今天他呕心写了这本书,我相信 这是目前质量最高的一本 Node 图书。因为中国没有几个人像朴灵一样,有机会在很多高并发的 应用场景中反复实践。这绝对是一本实践性极强的技术书,不管是否学习过 Node,只有你爱好 技术,都推荐你阅读它。 空无 CNode 社区创始人 阿里巴巴数据平台事业部数据交换平台总监 错误!文档中没有指定样式的文字。 1 1 2 3 5 7 10 12 8 9 4 6 11 序二 Node 诞生于 2009 年,天才的屌丝青年 Ryan Dahl 利用了 Google 的 V8 引擎打造了基于事件 循环实现的异步 I/O 框架。也许 Ryan 当时选择 JavaScript 作为服务器开发语言,只是因为 V8 的 性能远超其他脚本语言,但是这却成为 Node 成功的极其重要的因素。不仅仅是 JavaScript 巨大 的用户群,更重要的是 JavaScript 之前没有任何 I/O 库,这使 Node 在开发异步 I/O 时不会像 EventMachine、Twisted 那样因与同步 I/O 混用而导致问题。 短短几年的时间,Node 取得了巨大的成功。在开源社区 GitHub 上,Node 高居第二。express、 socket.io 这样的优秀框架都有着极高的排名,NPM 上的模块数量和下载量也非常惊人。更可喜的 是,国内的 Node 社区也诞生了许多优秀的开源项目,其中 node-webkit、pomelo 等在国际开源社 区中都产生了一定的影响力。 在企业界,Node 的应用也越来越广泛。LinkedIn 的移动平台已经全部从 Ruby 迁移到 Node, 机器数量缩减为原来的十分之一。像 Yahoo、Microsoft 这样的大公司,有好多应用已经迁移到 Node 了。国内的阿里巴巴、网易、腾讯、新浪、百度等公司的很多线上产品也纷纷改用 Node 开 发,并取得了很好的效果。 朴灵是国内最早的 Node 开发者之一,不仅组织了 CNode 社区,在 InfoQ 发表的“深入浅出 Node.js”系列文章更是对国内的 Node 社区产生了巨大的影响。记得我在 2011 年初次接触 Node 的时候,除了国外的几个演讲文稿,基本上没有 Node 相关的图书,而最让我印象深刻的,毫无 疑问是朴灵的“深入浅出 Node.js”系列文章。正是这一系列文章,使我们较好地理解、学习 Node 后,开发出了 pomelo 框架,也奠定了朴灵在国内 Node 界的地位。 如今两年过去了,国内外的 Node 图书也出了不少。但国内的几本书有点偏浅,即使国外的 几本名气很大的书也没有让我有动力通读全书,因为内容整体上没有太大深度,对于有较久开发 经验的 Node 开发者帮助不是很大。不过当朴灵让我审校这本书时,我觉得收获颇多。相比其他 Node 图书的作者,他在淘宝一线的开发经验使这本书更有深度,而他文艺青年的背景让这本书 读起来极其顺畅,他的钻研精神又让这本书在理论上很有深度。例如,朴灵在微博上自称“一个 能搞定回调函数嵌套的男人”还真不是吹的,在第 4 章中,他详细介绍了 Node 的各种嵌套函数 过深的解决方案,例如 EventProxy、Promise、async、step、wind.js 等各种解决方案都有深入讲 解。此外,朴灵还是 EventProxy 的作者,在这方面有最权威的实践经验。 朴灵是国内 Node 界的第一传道士,除了那一系列文章,他还在全国各地组织了 NodeParty 和 JSConf China(2012 年的沪 JS 和 2013 年的京 JS),并且在微博上以各种诙谐幽默的方法宣传 2 序二 Node。在各个技术大会上,我们都可以见到朴灵的身影。更强的是,朴灵在每次大会上所做的演 讲很少雷同,他总是能挖掘出 Node 的方方面面,然后很认真地总结出来,以幽默的讲解让听众 愉快地接受。 因此,当得知朴灵要写这本书时,我们都很兴奋。谁能比他更胜任呢?毫无疑问,这将是国 内第一的 Node 图书。如今,经过一年多的等待,你们终于有机会看到朴灵这一年多辛勤劳动的 成果了。 谢骋超 网易高级技术专家、架构师 pomelo 开源游戏服务器框架创始人 2013 年 7 月 8 日 前 言 1 1 2 3 5 7 10 12 8 9 4 6 11 前 言 2006 年至今,我们时常可以看到 JavaScript 的新闻,刚开始只是 JavaScript 引擎性能的提升, 到后来发现很多是来自 HTML5 和 Node 创造的奇迹。如果只看表面,很容易让人感觉这又是一 颗卫星。这种现象让人觉得不可信,所以出现了以下各种版本的误解。  Node 肯定是几个前端工程师在实验室里捣鼓出来的。  为了后端而后端,有意思吗?  怎么又发明了一门新语言?  JavaScript 承担的责任太重了。  直觉上,JavaScript 不应该运行在后端。  前端工程师要逆袭了。 一方面,大家看到 JavaScript 在各个地方放出异彩,其他语言的开发者既羡慕它的成果, 又担心它对当前所从事的语言造成冲击;另一方面,人们还是有 JavaScript 只能做前端脚本的 定势思维。究其原因,还是因为人们缺乏历史观层次上的认知,所以会产生一些莫须有的惴惴 不安。 1995 年,JavaScript 随网景公司发布的 Netscape Navigator 2.0 发布,它最早命名为 LiveScript, 随后更名为 JavaScript。它出自如今的 Mozilla 公司的 CTO——Brendan Eich 之手,其产生来源于 网景公司发布的 Netscape Navigator 浏览器需要一种脚本语言来协助浏览器做一些简单的动态操 作。当时网景公司与 Sun 公司合作密切,不懂技术的管理层希望得到一个 Java 的脚本版语言, 以期能像 Java 一样风靡。Brendan Eich 原本进入网景公司是希望做 Scheme 语言的开发,但是却 接到了一个不喜欢的任务,但迫于当时形势,不得不完成此事,于是 JavaScript 之父在 10 天的时 间里仓促完成了 JavaScript 的设计,当时的项目代号是 Mocha,名字叫 LiveScript。 这门语言除了看起来像 Java 外,本质与 Java 语言相去甚远,管理层期望的 Java Script 其实 借鉴了 C、Scheme、Self、Java 的设计。尽管仓促,但是这门语言还是借鉴了其他语言的不少优 点,如函数式、原型链继承等。处于 Java 阴影下的这门语言获得了它最终的名字:JavaScript。 至今,仍然还有许多人分不清 Java 与 JavaScript 的关系,就像分不清雷锋与雷峰塔一样。 虽然 JavaScript 的产生与 Netscape Navigator 浏览器的需求有关系,但它并非只是设计出来用 于浏览器前端的。早在 1994 年,网景公司就公布了其 Netscape Enterprise Server 中的一种服务器 端脚本实现,它的名字叫 LiveWire,是最早的服务器端 JavaScript,甚至早于浏览器中的 JavaScript 公布。对于这门图灵完备的语言,网景早就开始尝试将它用在后端。 2 前 言 随后,微软在第一次浏览器大战时,于1996年发布的IE 3.0中也包含了它的脚本语言:JScript。 基于商标的原因,它叫 JScript,但是与 JavaScript 兼容。在 1997 年年初,微软在它的服务器 IIS 3.0 中也包含了 JScript,这就是我们在 ASP 中能使用的脚本语言。鉴于微软处处与网景针锋相对, 出于保护自己的目的,网景公司推进了 JavaScript 的标准化进程,于 1996 年 11 月将 JavaScript 递交给 ECMA 国际标准组织,在 1997 年 7 月公布了第一个版本,是为 ECMA-262 号标准,又称 ECMAScript。 可以看到,JavaScript 一早就能运行在前后端,但风云变幻,在前后端各自的待遇却不尽相 同。伴随着 Java、PHP、.NET 等服务器端技术的风靡,与前端浏览器中的 JavaScript 越来越重要 相比,服务器端 JavaScript 逐渐式微。只剩下 Rhino、SpiderMonkey 用于工具。 然而,这个世界是变化的。第一次浏览器大战落幕后的 JavaScript 的世界有些平静,但依然 在萌生一些变化。Google 对 Ajax 的应用让 JavaScript 变得越来越重要。Firefox 的发布掀起了对 IE 的反攻,迎来了第二次浏览器大战,竞争令 JavaScript 的性能不断提升,Chrome 的加入令它 高潮迭出。CommonJS 规范的提出,不断在完善 JavaScript。ECMAScript 标准的不断推进,令语 言更加精炼简洁,不停地去芜存菁。 浏览器端 JavaScript 在 Web 应用中盛行,甚至让人们忘掉了 JavaScript 可以在服务器端运行 这码事。但是,服务器端 JavaScript 现在回来了,因为 Node 诞生了。Node 的诞生离不开上述的 历史契机,服务器端 JavaScript 在漫长的历史中长期停滞留下空白,但 Node 重新将这个领域激 活。Ryan Dahl 基于对高性能 Web 服务器的探索,无意间促成了服务器端 JavaScript 领域的焕然 一新。Node 凭借 V8 的高性能和异步 I/O 模型将 JavaScript 重新推向了一个高潮。现在,Node 不 仅满足 JavaScript 同时运行在前后端,而且性能还十分高效。与传统印象中的不同,它甚至可比 于当前的高效脚本语言。 奇妙的反应还在继续,前后端要跨语言开发的现状已经开始改变,因为语言堆栈的不同,开 发者的分工也进行了细分:前端工程师和后端工程师。专业技能因为分工而精进,但也将技能变 为专利,似乎前端工程师不能进行后端开发,后端工程师搞不定前端开发,犹如树立的墙。但 Node 的出现令这种分工的界限又开始模糊了。同时一些后端工程师也关注到 Node,他们甚至不 关心前后端语言是否一致,而是赤裸裸地表示对 Node 高性能的垂涎,如实时、高并发等。 大量的前后端工程师加入了 Node 的开发阵营,GitHub 上 JavaScript 是最活跃的开发语言, NPM 社区第三方模块恐怖的增长速度和下载量都昭示着这个过程不可逆,在这里吼一声万能的 NPM,总能找到你需要的解决方案。很多不断涌现的项目和创意都因为 Node 和前端开发能共用 一种语言而独特。换言之,Node 的本意是提供一个高性能的面向网络的执行平台,但无意间促 成了 JavaScript 社区的繁荣,并进而形成强大的生态系统。 本书目的 目前,还没有一本书将 Node 自身结构介绍出来,大多停留在 Node 介绍或者框架、库的使用 层面上,本书希望从不同的视角揭示 Node 自己内在的特点和结构。也许你已经用过 Node 进行 前 言 3 1 2 3 5 7 10 12 8 9 4 6 11 相关的开发,在使用了 Node 带来的欣喜后,还能在阅读本书时,发出一句“哦,原来 Node 是 这样的”,这就是本书的简单寄望。 对于 Node 初学者,目前市面上也已经有 Node 相关的入门书,它们可以快速地领你进入 Node 开发之旅。在了解了这些基本过程后,想了解更多 Node 知识的好奇心,会领你来阅读本 书的。 阅读建议 本书并非完全按照顺序递进式介绍,如第 2 章是从代码组织结构看待 Node,第 3 章是从运 行结构看 Node,第 4 章则是从编程结构看 Node,第 5 章则是 Node 中内存结构的揭示,第 6 章 谈及的是 Node 中的数据在 I/O 流中的结构或状态,第 7 章是 Node 在网络服务角度的介绍,第 8 章是 Node 在 HTTP 上的展现,第 9 章讨论了 Node 的单机集群结构,第 10 章是从单元测试和性 能测试的角度去关注 Node,第 11 章虽然已经脱离了 Node 编码的范畴,但是站在产品化的角度 看待 Node,也会颇有收获。 下面是各章的详细介绍。 第 1 章:这一章简要介绍了 Node,从中可以了解 Node 的发展历程及其带来的影响和价值。 第 2 章:这一章介绍了 Node 的模块机制,从中可以了解到 Node 是如何实现 CommonJS 模 块和包规范的。在这一章中,我们详细解释了模块在引用过程中的编译、加载规则。另外,我们 还能读到更深度的关于 Node 自身源代码的组织架构。 第 3 章:这一章展示了在 Node 中我们将异步 I/O 作为主要设计理念的原因。另外,还会介 绍到异步 I/O 的详细实现过程。 第 4 章:这一章主要介绍异步编程,其中有常见的异步编程问题介绍,也有详细的解决方案。 在这一章中,我们可以接触到 Promise、事件、高阶函数是如何进行流程控制的。 第 5 章:这一章主要介绍了 Node 中的内存控制,主要内容有垃圾回收、内存限制、查看内 存、内存泄漏、大内存应用等细节。 第 6 章:这一章介绍了前端 JavaScript 里不能遇到的 Buffer。由 于 Node 中会涉及频繁的网络 和磁盘 I/O,处理字节流数据会是很常见的行为,这部分场景与纯粹的前端开发完全不同。 第 7 章:这一章介绍了 Node 支持的 TCP、UDP、HTTP 编程,还附赠了 WebSocket 与 TLS、 HTTPS 的介绍。 第 8 章:这一章介绍了构建 Web 应用的过程中用到的大多数技术细节,如数据处理、路由、 MVC、模板、RESTful 等 第 9 章:这一章介绍了 Node 的多进程技术,以及如何借助多进程的方式来提升应用的可用 性和性能。 第 10 章:这一章介绍了 Node 的单元测试和性能测试技巧。 第 11 章:“行百里者半九十”,完成产品开发的代码编写后,才完成了项目的第一步。这一 4 前 言 章介绍了将 Node 产品化所需要注意到的细节,如项目工程化、代码部署、日志、性能、监控报 警、稳定性、异构共存等。 附录 A:详细介绍了 Node 的安装步骤。 附录 B:讨论了 Node 的调试技巧。 附录 C:探讨了团队实践或多人协作过程中需要关注的编码规范问题,它可以很好地规避一 些低级的、明显的错误。 附录 D:作为企业开发者,必须关注模块仓库的搭建管理。在这一章中,我们介绍了如何通 过搭建私有 NPM 来解决企业隐私安全等方面的问题。 目 录 1 目 录 第 1 章 Node 简介 ...........................................1 1.1 Node 的诞生历程.......................................1 1.2 Node 的命名与起源...................................1 1.2.1 为什么是JavaScript.......................2 1.2.2 为什么叫Node...............................2 1.3 Node 给 JavaScript 带来的意义.................2 1.4 Node 的特点 ..............................................4 1.4.1 异步I/O..........................................4 1.4.2 事件与回调函数.............................6 1.4.3 单线程............................................7 1.4.4 跨平台............................................7 1.5 Node 的应用场景.......................................8 1.5.1 I/O 密集型......................................8 1.5.2 是否不擅长 CPU 密集型业务 ........8 1.5.3 与遗留系统和平共处 ...................10 1.5.4 分布式应用 ..................................10 1.6 Node 的使用者.........................................10 1.7 参考资源..................................................11 第 2 章 模块机制............................................12 2.1 CommonJS 规范.......................................13 2.1.1 CommonJS 的出发点 ...................13 2.1.2 CommonJS 的模块规范................14 2.2 Node 的模块实现.....................................15 2.2.1 优先从缓存加载...........................16 2.2.2 路径分析和文件定位 ...................16 2.2.3 模块编译......................................18 2.3 核心模块..................................................20 2.3.1 JavaScript 核心模块的编译 过程..............................................21 2.3.2 C/C++核心模块的编译过程.........22 2.3.3 核心模块的引入流程...................25 2.3.4 编写核心模块 ..............................25 2.4 C/C++扩展模块.......................................27 2.4.1 前提条件......................................28 2.4.2 C/C++扩展模块的编写................29 2.4.3 C/C++扩展模块的编译................30 2.4.4 C/C++扩展模块的加载................31 2.5 模块调用栈..............................................32 2.6 包与NPM................................................33 2.6.1 包 结构..........................................34 2.6.2 包描述文件与 NPM .....................34 2.6.3 NPM 常用功能.............................37 2.6.4 局域NPM ....................................42 2.6.5 NPM 潜在问题.............................43 2.7 前后端共用模块 ......................................44 2.7.1 模块的侧重点 ..............................44 2.7.2 AMD 规范....................................44 2.7.3 CMD 规范 ....................................45 2.7.4 兼容多种模块规范.......................45 2.8 总结.........................................................46 2.9 参考资源..................................................46 第 3 章 异步 I/O .............................................47 3.1 为什么要异步 I/O....................................47 3.1.1 用户体验......................................48 3.1.2 资源分配......................................49 3.2 异步I/O 实现现状...................................50 3.2.1 异步I/O 与非阻塞 I/O .................50 3.2.2 理想的非阻塞异步 I/O.................54 3.2.3 现实的异步 I/O............................54 3.3 Node 的异步 I/O......................................56 2 目 录 3.3.1 事件循环......................................56 3.3.2 观察者..........................................56 3.3.3 请求对象......................................57 3.3.4 执行回调......................................59 3.3.5 小结..............................................60 3.4 非I/O 的异步 API ...................................60 3.4.1 定时器..........................................60 3.4.2 process.nextTick()......................61 3.4.3 setImmediate().............................62 3.5 事件驱动与高性能服务器.......................63 3.6 总结.........................................................65 3.7 参考资源 .................................................65 第 4 章 异步编程............................................66 4.1 函数式编程..............................................66 4.1.1 高阶函数......................................66 4.1.2 偏函数用法 ..................................67 4.2 异步编程的优势与难点...........................68 4.2.1 优势..............................................69 4.2.2 难点..............................................70 4.3 异步编程解决方案 ..................................74 4.3.1 事件发布/订阅模式......................74 4.3.2 Promise/Deferred 模式.................82 4.3.3 流程控制库 ..................................93 4.4 异步并发控制........................................105 4.4.1 bagpipe 的解决方案...................105 4.4.2 async 的解决方案 ......................109 4.5 总结.......................................................110 4.6 参考资源 ...............................................110 第 5 章 内存控制..........................................111 5.1 V8 的垃圾回收机制与内存限制 ...........111 5.1.1 Node 与 V8.................................112 5.1.2 V8 的内存限制...........................112 5.1.3 V8 的对象分配...........................112 5.1.4 V8 的垃圾回收机制...................113 5.1.5 查看垃圾回收日志.....................119 5.2 高效使用内存........................................121 5.2.1 作用域........................................121 5.2.2 闭包............................................123 5.2.3 小结............................................124 5.3 内存指标 ............................................... 124 5.3.1 查看内存使用情况 .................... 124 5.3.2 堆外内存.................................... 126 5.3.3 小结........................................... 127 5.4 内存泄漏 ............................................... 127 5.4.1 慎将内存当做缓存 .................... 127 5.4.2 关注队列状态 ............................ 130 5.5 内存泄漏排查........................................ 130 5.5.1 node-heapdump.......................... 131 5.5.2 node-memwatch ......................... 132 5.5.3 小结........................................... 135 5.6 大内存应用 ........................................... 135 5.7 总结 ....................................................... 136 5.8 参考资源 ............................................... 136 第 6 章 理解 Buffer ..................................... 137 6.1 Buffer 结构............................................ 137 6.1.1 模块结构.................................... 137 6.1.2 Buffer 对象 ................................ 138 6.1.3 Buffer 内存分配......................... 139 6.2 Buffer 的转换 ........................................ 141 6.2.1 字符串转Buffer......................... 141 6.2.2 Buffer 转字符串......................... 142 6.2.3 Buffer 不支持的编码类型 ......... 142 6.3 Buffer 的拼接 ........................................ 143 6.3.1 乱码是如何产生的 .................... 144 6.3.2 setEncoding()与 string_ decoder()................................... 144 6.3.3 正确拼接Buffer......................... 145 6.4 Buffer 与性能 ........................................ 146 6.5 总结....................................................... 149 6.6 参考资源 ............................................... 149 第 7 章 网络编程 ......................................... 150 7.1 构建TCP 服务 ...................................... 150 7.1.1 TCP............................................ 150 7.1.2 创建TCP 服务器端 ................... 151 7.1.3 TCP 服务的事件........................ 153 7.2 构建UDP 服务...................................... 154 7.2.1 创建UDP 套接字 ...................... 154 目 录 3 7.2.2 创建UDP 服务器端 ...................154 7.2.3 创建UDP 客户端.......................155 7.2.4 UDP 套接字事件........................155 7.3 构建HTTP 服务 ....................................155 7.3.1 HTTP..........................................156 7.3.2 http 模块....................................157 7.3.3 HTTP 客户端..............................161 7.4 构建WebSocket 服务 ............................163 7.4.1 WebSocket 握手 .........................164 7.4.2 WebSocket 数据传输..................167 7.4.3 小结............................................169 7.5 网络服务与安全 ....................................169 7.5.1 TLS/SSL.....................................170 7.5.2 TLS 服务 ....................................172 7.5.3 HTTPS 服务 ...............................173 7.6 总结.......................................................175 7.7 参考资源................................................176 第 8 章 构建 Web 应用 ...............................177 8.1 基础功能................................................177 8.1.1 请求方法....................................178 8.1.2 路径解析....................................179 8.1.3 查询字符串 ................................180 8.1.4 Cookie ........................................181 8.1.5 Session........................................184 8.1.6 缓存............................................190 8.1.7 Basic 认证 ..................................193 8.2 数据上传................................................195 8.2.1 表单数据....................................195 8.2.2 其他格式....................................196 8.2.3 附件上传....................................197 8.2.4 数据上传与安全.........................199 8.3 路由解析................................................201 8.3.1 文件路径型 ................................202 8.3.2 MVC...........................................202 8.3.3 RESTful......................................207 8.4 中间件....................................................210 8.4.1 异常处理....................................214 8.4.2 中间件与性能.............................215 8.4.3 小结............................................216 8.5 页面渲染................................................217 8.5.1 内容响应....................................217 8.5.2 视图渲染....................................219 8.5.3 模板............................................220 8.5.4 Bigpipe.......................................231 8.6 总结.......................................................235 8.7 参考资源................................................235 第 9 章 玩转进程..........................................236 9.1 服务模型的变迁 ....................................236 9.1.1 石器时代:同步.........................236 9.1.2 青铜时代:复制进程.................237 9.1.3 白银时代:多线程.....................237 9.1.4 黄金时代:事件驱动.................237 9.2 多进程架构............................................238 9.2.1 创建子进程 ................................239 9.2.2 进程间通信 ................................240 9.2.3 句柄传递....................................242 9.2.4 小结............................................247 9.3 集群稳定之路........................................248 9.3.1 进程事件....................................248 9.3.2 自动重启....................................249 9.3.3 负载均衡....................................254 9.3.4 状态共享....................................255 9.4 Cluster 模块 ...........................................257 9.4.1 Cluster 工作原理........................258 9.4.2 Cluster 事件................................259 9.5 总结.......................................................259 9.6 参考资源................................................260 第 10 章 测试................................................261 10.1 单元测试..............................................261 10.1.1 单元测试的意义.....................261 10.1.2 单元测试介绍.........................263 10.1.3 工程化与自动化.....................276 10.1.4 小结........................................277 10.2 性能测试..............................................278 10.2.1 基准测试 ................................278 10.2.2 压力测试 ................................280 10.2.3 基准测试驱动开发.................281 10.2.4 测试数据与业务数据的转换 ...283 4 目 录 10.3 总结.....................................................284 10.4 参考资源..............................................284 第 11 章 产品化............................................285 11.1 项目工程化..........................................285 11.1.1 目录结构 ................................285 11.1.2 构建工具 ................................286 11.1.3 编码规范 ................................289 11.1.4 代码审查 ................................289 11.2 部署流程..............................................290 11.2.1 部署环境 ................................291 11.2.2 部署操作 ................................291 11.3 性能.....................................................293 11.3.1 动静分离 ................................293 11.3.2 启用缓存 ................................294 11.3.3 多进程架构 ............................294 11.3.4 读写分离 ................................295 11.4 日志.....................................................295 11.4.1 访问日志 ................................295 11.4.2 异常日志 ................................296 11.4.3 日志与数据库 ........................299 11.4.4 分割日志 ................................299 11.4.5 小结........................................299 11.5 监控报警..............................................299 11.5.1 监控........................................300 11.5.2 报警的实现 ............................302 11.5.3 监控系统的稳定性.................303 11.6 稳定性.................................................303 11.7 异构共存..............................................304 11.8 总结.....................................................305 11.9 参考资源..............................................305 附录 A 安装Node .......................................306 A.1 Windows 系统下的 Node 安装 .............306 A.2 Mac 系统下 Node 的安装 .....................307 A.3 Linux 系统下 Node 的安装...................308 A.4 总结......................................................309 A.5 参考资源...............................................309 附录 B 调试Node ................................ 310 B.1 Debugger............................................... 310 B.2 Node Inspector...................................... 311 B.2.1 安装Node Inspector................. 312 B.2.2 错误堆栈 .................................. 312 B.3 总结...................................................... 313 附录 C Node 编码规范............................... 314 C.1 根源...................................................... 314 C.2 编码规范............................................... 315 C.2.1 空格与格式 .............................. 315 C.2.2 命名规范 .................................. 317 C.2.3 比较操作 .................................. 318 C.2.4 字面量...................................... 318 C.2.5 作用域...................................... 318 C.2.6 数组与对象 .............................. 319 C.2.7 异步.......................................... 320 C.2.8 类与模块 .................................. 320 C.2.9 注解规范 .................................. 321 C.3 最佳实践............................................... 321 C.3.1 冲突的解决原则....................... 321 C.3.2 给编辑器设置检测工具 ........... 321 C.3.3 版本控制中的 hook.................. 322 C.3.4 持续集成 .................................. 322 C.4 总结...................................................... 322 C.5 参考资源............................................... 323 附录 D 搭建局域NPM 仓库...................... 324 D.1 NPM 仓库的安装.................................. 325 D.1.1 安装Erlang 和 CouchDB ......... 325 D.1.2 搭建NPM 仓库........................ 326 D.2 高阶应用 .............................................. 328 D.2.1 镜像仓库.................................. 328 D.2.2 私有模块应用 .......................... 328 D.2.3 纯私有仓库 .............................. 329 D.3 总结...................................................... 331 D.4 参考资源 .............................................. 332 1.2 Node 的命名与起源 1 1 2 3 4 5 11 6 7 8 9 10 Node简介 Node应该是如今最火热的技术了,从本章开始,我们将逐步揭示它的诸多细节。 1.1 Node 的诞生历程 Node的诞生历程如下所示。  2009年3月,Ryan Dahl在其博客上宣布准备基于V8创建一个轻量级的Web服务器并提供一 套库。  2009年5月,Ryan Dahl在GitHub上发布了最初的版本。  2009年12月和2010年4月,两届JSConf大会都安排了Node的讲座。  2010年年底,Node获得硅谷云计算服务商Joyent公司的资助,其创始人Ryan Dahl加入 Joyent公司全职负责Node的发展。  2011年7月,Node在微软的支持下发布了其Windows版本。  2011年11月,Node超越Ruby on Rails,成为GitHub上关注度最高的项目(随后被Bootstrap 项目超越,目前仍居第二)。  2012年1月底,Ryan Dahl在对Node架构设计满意的情况下,将掌门人的身份转交给Isaac Z. Schlueter,自己转向一些研究项目。Isaac Z. Schlueter是Node的包管理器NPM的作者,之 后Node的版本发布和bug修复等工作由他接手。  截至笔者执笔之日(2013年7月13日),发 布 的 Node稳定版为v0.10.13,非稳定版为v0.11.4, NPM的官方模块数达到34 943个,模块的周下载量为1479万次。  随后,Node的发布计划主要集中在性能提升上,在v0.14之后,正式发布出v1.0版本。 1.2 Node 的命名与起源 在Node的官方网站(http://nodejs.org)之外,Node具有很多别称:Nodejs、NodeJS、Node.js 等。本书在写作过程中遵循官方的说法,将会一直使用Node这个名字,但是在当前语境之外, 为了与其余表示节点的技术或名词相区别,均可以带上.js表明它是Node。在听到这些词汇时, 第1章 2 第1 章 Node 简介 应该意识到,它们说的是一码事。除了本书的封面和此处会用到Node.js外,其余地方都会以 Node作为正式称谓。 Node名字的来由,其实跟它的起源是有密切关系的。 1.2.1 为什么是JavaScript Ryan Dahl是一名资深的C/C++程序员,在创造出Node之前,他的主要工作都是围绕高性能 Web服务器进行的。经历过一些尝试和失败之后,他找到了设计高性能,Web服务器的几个要点: 事件驱动、非阻塞I/O。 所以Ryan Dahl最初的目标是写一个基于事件驱动、非阻塞I/O的Web服务器,以达到更高的 性能,提供Apache等服务器之外的选择。他提到,大多数人不设计一种更简单和更有效率的程序 的主要原因是他们用到了阻塞I/O的库。写作Node的时候,Ryan Dahl曾经评估过C、Lua、Haskell、 Ruby等语言作为备选实现,结论为:C的开发门槛高,可以预见不会有太多的开发者能将它用于 日常的业务开发,所以舍弃它;Ryan Dahl觉得自己还不足够玩转Haskell,所以舍弃它;Lua自身 已经含有很多阻塞I/O库,为其构建非阻塞I/O库也不能改变人们继续使用阻塞I/O库的习惯,所以 也舍弃它;而Ruby的虚拟机由于性能不好而落选。 相比之下,JavaScript比C的开发门槛要低,比Lua的历史包袱要少。尽管服务器端JavaScript 存在已经很多年了,但是后端部分一直没有市场,可以说历史包袱为零,为其导入非阻塞I/O库 没有额外阻力。另外,JavaScript在浏览器中有广泛的事件驱动方面的应用,暗合Ryan Dahl喜好 基于事件驱动的需求。当时,第二次浏览器大战也渐渐分出高下,Chrome浏览器的JavaScript引 擎V8摘得性能第一的桂冠,而且其基于新BSD许可证发布,自然受到Ryan Dahl的欢迎。考虑到 高性能、符合事件驱动、没有历史包袱这3个主要原因,JavaScript成为了Node的实现语言。 1.2.2 为什么叫Node 起初,Ryan Dahl称他的项目为web.js,就是一个Web服务器,但是项目的发展超过了他最初 单纯开发一个Web服务器的想法,变成了构建网络应用的一个基础框架,这样可以在它的基础上 构建更多的东西,诸如服务器、客户端、命令行工具等。Node发展为一个强制不共享任何资源的 单线程、单进程系统,包含十分适宜网络的库,为构建大型分布式应用程序提供基础设施,其目 标也是成为一个构建快速、可伸缩的网络应用平台。它自身非常简单,通过通信协议来组织许多 Node,非常容易通过扩展来达成构建大型网络应用的目的。每一个Node进程都构成这个网络应 用中的一个节点,这是它名字所含意义的真谛。 1.3 Node 给 JavaScript 带来的意义 V8给Chrome浏览器带来了一个强劲的心脏,使得它在浏览器大战中脱颖而出,也使得Ryan Dahl在语言评估中为选择JavaScript增加了一个极大的权重值。这里我们要谈谈Node给JavaScript 1.3 Node 给 JavaScript 带来的意义 3 1 2 3 4 5 11 6 7 8 9 10 带来的一个新局面。鉴于Node之前那些不给力的后端JavaScript实现,在性能和编程模型等方面 没能达到与其他语言一较高下的程度,这里先撇开不谈,先谈谈Node与浏览器的对比。 Chrome浏览器和Node的组件构成如图1-1所示。我们知道浏览器中除了V8作为JavaScript引擎 外,还有一个WebKit布局引擎。 HTML5在发展过程中定义了更多更丰富的API。在实现上,浏 览器提供了越来越多的功能暴露给JavaScript和HTML标签。这个愿景美好,但对于前端浏览器的 发展现状而言,HTML5标准统一的过程是相对缓慢的。JavaScript作为一门图灵完备的语言,长 久以来却限制在浏览器的沙箱中运行,它的能力取决于浏览器中间层提供的支持有多少。 图1-1 Chrome浏览器和Node的组件构成 除了HTML、WebKit和显卡这些UI相关技术没有支持外,Node的结构与Chrome十分相似。 它们都是基于事件驱动的异步架构,浏览器通过事件驱动来服务界面上的交互,Node通过事件驱 动来服务I/O,这个细节将在第3章中详述。在Node中,JavaScript可以随心所欲地访问本地文件, 可以搭建WebSocket服务器端,可以连接数据库,可以如Web Workers一样玩转多进程。如今, JavaScript可以运行在不同的地方,不再继续限制在浏览器中与CSS样式表、DOM树打交道。如 果HTTP协议栈是水平面,Node就是浏览器在协议栈另一边的倒影。Node不处理UI,但用与浏览 器相同的机制和原理运行。Node打破了过去JavaScript只能在浏览器中运行的局面。前后端编程 环境统一,可以大大降低前后端转换所需要的上下文交换代价。 对于前端工程师而言,自己所熟悉的JavaScript如今竟然可以在另一个地方放出异彩,不谈其 他原因,仅仅因为好奇,就值得去关注和探究它。 随着Node的出现,关于JavaScript的想象总是无限的。目前,社区已经出现 node-webkit这样的项目,这个项目在2012年的沪JS会议上首次介绍给了公众。如同上文 提及的关于浏览器的优势和限制,在node-webkit项目中,它将Node中的事件循环和 WebKit的事件循环融合在一起,既可以通过它享受HTML、CSS带来的UI构建,也能通 过它访问本地资源,将两者的优势整合到一起。桌面应用程序的开发可以完全通过 HTML、CSS、JavaScript完成。 4 第1 章 Node 简介 1.4 Node 的特点 作为后端JavaScript的运行平台,Node保留了前端浏览器JavaScript中那些熟悉的接口,没有 改写语言本身的任何特性,依旧基于作用域和原型链,区别在于它将前端中广泛运用的思想迁移 到了服务器端。下面我们来看看Node相较其他语言的一些特点。 1.4.1 异步I/O 关于异步I/O,向前端工程师解释起来或许会容易一些,因为发起Ajax调用对于前端工程师 而言是再熟悉不过的场景了。下面的代码用于发起一个Ajax请求: $.post('/url', {title: '深入浅出Node.js'}, function (data) { console.log('收到响应'); }); console.log('发送Ajax结束'); 熟悉异步的用户必然知道,“收到响应”是在“发送Ajax结束”之后输出的。在调用$.post() 后,后续代码是被立即执行的,而“收到响应”的执行时间是不被预期的。我们只知道它将在这 个异步请求结束后执行,但并不知道具体的时间点。异步调用中对于结果值的捕获是符合“Don’t call me, I will call you”的原则的,这也是注重结果,不关心过程的一种表现。图1-2是一个经典 的Ajax调用。 图1-2 经典的Ajax调用 在Node中,异步I/O也很常见。以读取文件为例,我们可以看到它与前端Ajax调用的方式是 极其类似的: 1.4 Node 的特点 5 1 2 3 4 5 11 6 7 8 9 10 var fs = require('fs'); fs.readFile('/path', function (err, file) { console.log('读取文件完成') }); console.log('发起读取文件'); 这里的“发起读取文件”是在“读取文件完成”之前输出的。同样,“读取文件完成”的执 行也取决于读取文件的异步调用何时结束。图1-3是一个经典的异步调用。 图1-3 经典的异步调用 在Node中,绝大多数的操作都以异步的方式进行调用。Ryan Dahl排除万难,在底层构建了 很多异步I/O的API,从文件读取到网络请求等,均是如此。这样的意义在于,在Node中,我们可 以从语言层面很自然地进行并行I/O操作。每个调用之间无须等待之前的I/O调用结束。在编程模 型上可以极大提升效率。 下面的两个文件读取任务的耗时取决于最慢的那个文件读取的耗时: fs.readFile('/path1', function (err, file) { console.log('读取文件1完成'); }); fs.readFile('/path2', function (err, file) { console.log('读取文件2完成'); }); 而对于同步I/O而言,它们的耗时是两个任务的耗时之和。这里异步带来的优势是显而易 见的。 关于异步I/O是如何提升效率的及其本身的机制和实现,我们将在第3章中详述。 6 第1 章 Node 简介 1.4.2 事件与回调函数 随着Web 2.0时代的到来,JavaScript在前端担任了更多的职责,事件也得到了广泛的应用。 Node不像Rhino那样受Java的影响很大,而是将前端浏览器中应用广泛且成熟的事件引入后端, 配合异步I/O,将事件点暴露给业务逻辑。 下面的例子展示的是Ajax异步提交的服务器端处理过程。Node创建一个Web服务器,并侦听 8080端口。对于服务器,我们为其绑定了request事件,对于请求对象,我们为其绑定了data事 件和end事件: var http = require('http'); var querystring = require('querystring'); // 侦听服务器的request事件 http.createServer(function (req, res) { var postData = ''; req.setEncoding('utf8'); // 侦听请求的data事件 req.on('data', function (trunk) { postData += trunk; }); // 侦听请求的end事件 req.on('end', function () { res.end(postData); }); }).listen(8080); console.log('服务器启动完成'); 相应地,我们在前端为Ajax请求绑定了success事件,在发出请求后,只需关心请求成功时 执行相应的业务逻辑即可,相关代码如下: $.ajax({ 'url': '/url', 'method': 'POST', 'data': {}, 'success': function (data) { // success事件 } }); 相比之下,无论在前端还是后端,事件都是常用的。对于其他语言来说,这种俯拾皆是 JavaScript的熟悉感觉是基本不会出现的。 事件的编程方式具有轻量级、松耦合、只关注事务点等优势,但是在多个异步任务的场景下, 事件与事件之间各自独立,如何协作是一个问题。 从前面可以看到,回调函数无处不在。这是因为在JavaScript中,我们将函数作为第一等公民 来对待,可以将函数作为对象传递给方法作为实参进行调用。 与其他的Web后端编程语言相比,Node除了异步和事件外,回调函数是一大特色。纵观下来, 回调函数也是最好的接受异步调用返回数据的方式。但是这种编程方式对于很多习惯同步思路编 程的人来说,也许是十分不习惯的。代码的编写顺序与执行顺序并无关系,这对他们可能造成阅 1.4 Node 的特点 7 1 2 3 4 5 11 6 7 8 9 10 读上的障碍。在流程控制方面,因为穿插了异步方法和回调函数,与常规的同步方式相比,变得 不那么一目了然了。 在转变为异步编程思维后,通过对业务的划分和对事件的提炼,在流程控制方面处理业务的 复杂度与同步方式实际上是一致的。 关于流程控制和事件协作的方法和技巧,我们将在第4章中进一步探讨。 1.4.3 单线程 Node保持了JavaScript在浏览器中单线程的特点。而且在Node中,JavaScript与其余线程是无 法共享任何状态的。单线程的最大好处是不用像多线程编程那样处处在意状态的同步问题,这里 没有死锁的存在,也没有线程上下文交换所带来的性能上的开销。 同样,单线程也有它自身的弱点,这些弱点是学习Node的过程中必须要面对的。积极面对这 些弱点,可以享受到Node带来的好处,也能避免潜在的问题,使其得以高效利用。单线程的弱点 具体有以下3方面。  无法利用多核CPU。  错误会引起整个应用退出,应用的健壮性值得考验。  大量计算占用CPU导致无法继续调用异步I/O。 像浏览器中JavaScript与UI共用一个线程一样,JavaScript长时间执行会导致UI的渲染和响应 被中断。在Node中,长时间的CPU占用也会导致后续的异步I/O发不出调用,已完成的异步I/O的 回调函数也会得不到及时执行。 最早解决这种大计算量问题的方案是Google公司开发的Gears。它启用一个完全独立的进程, 将需要计算的程序发送给这个进程,在结果得出后,通过事件将结果传递回来。这个模型将计算 量分发到其他进程上,以此来降低运算造成阻塞的几率。后来,HTML5定制了Web Workers的标 准,Google放弃了Gears,全力支持Web Workers。Web Workers能够创建工作线程来进行计算,以 解决JavaScript大计算阻塞UI渲染的问题。工作线程为了不阻塞主线程,通过消息传递的方式来 传递运行结果,这也使得工作线程不能访问到主线程中的UI。 Node采用了与Web Workers相同的思路来解决单线程中大计算量的问题:child_process。 子进程的出现,意味着Node可以从容地应对单线程在健壮性和无法利用多核CPU方面的问 题。通过将计算分发到各个子进程,可以将大量计算分解掉,然后再通过进程之间的事件消息来 传递结果,这可以很好地保持应用模型的简单和低依赖。通过Master-Worker的管理方式,也可以 很好地管理各个工作进程,以达到更高的健壮性。 关于如何通过子进程来充分利用硬件资源和提升应用的健壮性,这是一个值得探究的话题。 怎样才能使我们既享受到无忧无虑的单线程编程,又高效利用资源呢?请挪步到第9章。 1.4.4 跨平台 起初,Node只可以在Linux平台上运行。如果想在Windows平台上学习和使用Node,则必须 8 第1 章 Node 简介 通过Cygwin或者MinGW。随着Node的发展,微软注意到了它的存在,并投入了一个团队帮助Node 实现Windows平台的兼容,在v0.6.0版本发布时,Node已经能够直接在Windows平台上运行了。 图1-4是Node基于libuv实现跨平台的架构示意图。 图1-4 Node基于libuv实现跨平台的架构示意图 兼容Windows和*nix平台主要得益于Node在架构层面的改动,它在操作系统与Node上层模块 系统之间构建了一层平台层架构,即libuv。目前,libuv已经成为许多系统实现跨平台的基础组件。 关于libuv的设计,我们将在第3章中介绍。 通过良好的架构,Node的第三方C++模块也可以借助libuv实现跨平台。目前,除了没有保持 更新的C++模块外,大部分C++模块都能实现跨平台的兼容。 1.5 Node 的应用场景 在进行技术选型之前,需要了解一项新技术具体适合什么样的场景,毕竟合适的技术用在合 适的场景可以起到意想不到的效果。关于Node,探讨得较多的主要有I/O密集型和CPU密集型。 1.5.1 I/O密集型 在Node的推广过程中,无数次有人问起Node的应用场景是什么。如果将所有的脚本语言拿 到一处来评判,那么从单线程的角度来说,Node处理I/O的能力是值得竖起拇指称赞的。通常, 说Node擅长I/O密集型的应用场景基本上是没人反对的。Node面向网络且擅长并行I/O,能够有效 地组织起更多的硬件资源,从而提供更多好的服务。 I/O密集的优势主要在于Node利用事件循环的处理能力,而不是启动每一个线程为每一个请 求服务,资源占用极少。 1.5.2 是否不擅长CPU密集型业务 换一个角度,在CPU密集的应用场景中,Node是否能胜任呢?实际上,V8的执行效率是十 分高的。单以执行效率来做评判,V8的执行效率是毋庸置疑的。 1.5 Node 的应用场景 9 1 2 3 4 5 11 6 7 8 9 10 我们将相同的斐波那契数列计算(F0=0,F1=1,Fn=F(n1)+F(n2)(n≥2))分别用各种脚本语言 写了算法实现,并进行了n = 40的计算,以比较性能。这个测试主要偏重CPU栈操作,表1-1是其 中一次运算耗时的排行。在这些脚本语言中(其中C和Go语言是静态语言,用于参考),Node是 足够高效的,它优秀的运算能力主要来自V8的深度性能优化。 表1-1 计算斐波那契数列的耗时排行 语 言 用户态时间 排 名 版 本 C with -O2 0m0.202s #0 i686-apple-darwin11-llvm-gcc-4.2 (GCC) 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2336.11.00) Node(C++模块) 0m1.001s #1 v0.8.8, gcc -O2 Java 0m1.305s #2 Java(TM) SE Runtime Environment (build 1.6.0_35-b10-428-11M3811) Java HotSpot(TM) 64-Bit Server VM (build 20.10-b01-428, mixed mode) Go 0m1.667s #3 Go version go1.0.2 Scala 0m1.808s #4 Scala code runner version 2.9.2 -- Copyright 2002-2011, LAMP/EPFL LuaJIT 0m2.579s #5 LuaJIT 2.0.0-beta10 -- Copyright (C) 2005-2012 Mike Pall. Node 0m2.872s #6 v0.8.8 Ruby 2.0.0-p0 0m27.777s #7 ruby 2.0.0p0 (2013-02-24 revision 39474) [x86_64-darwin12.2.0] pypy 0m30.010s #8 Python 2.7.2 (341e1e3821ff, Jun 07 2012, 15:42:54) [PyPy 1.9.0 with GCC 4.2.1] Ruby 1.9.x 0m37.404s #9 ruby 1.9.3p194 (2012-04-20 revision 35410) [x86_64-darwin12.1.0] Lua 0m40.709s #10 Lua 5.1.4 Copyright (C) 1994-2008 Lua.org, PUC-Rio Jython 0m53.699s #11 Jython 2.5.2 PHP 1m17.728s #12 PHP 5.4.6 (cli) (built: Sep 8 2012 23:49:53) Python 1m17.979s #13 Python 2.7.2 Perl 2m41.259s #14 This is perl 5, version 12, subversion 4 (v5.12.4) built for darwin-thread- multi-2level Ruby 1.8.x 3m35.135s #15 ruby 1.8.7 (2012-02-08 patchlevel 358) [universal-darwin12.0] 这样的测试结果尽管不能完全反映出各个语言的性能优劣,但已经可以表明Node在性能上不 俗的表现。从另一个角度来说,这可以表明CPU密集型应用其实并不可怕。CPU密集型应用给Node 带来的挑战主要是:由于JavaScript单线程的原因,如果有长时间运行的计算(比如大循环),将 会导致CPU时间片不能释放,使得后续I/O无法发起。但是适当调整和分解大型运算任务为多个 小任务,使得运算能够适时释放,不阻塞I/O调用的发起,这样既可同时享受到并行异步I/O的好 处,又能充分利用CPU。 关于CPU密集型应用,Node的异步I/O已经解决了在单线程上CPU与I/O之间阻塞无法重叠 利用的问题,I/O阻塞造成的性能浪费远比CPU的影响小。对于长时间运行的计算,如果它的 耗时超过普通阻塞I/O的耗时,那么应用场景就需要重新评估,因为这类计算比阻塞I/O还影响 效率,甚至说就是一个纯计算的场景,根本没有I/O。此类应用场景或许应当采用多线程的方 式进行计算。Node虽然没有提供多线程用于计算支持,但是还是有以下两个方式来充分利 用CPU。  Node可以通过编写C/C++扩展的方式更高效地利用CPU,将一些V8不能做到性能极致的 10 第1 章 Node 简介 地方通过C/C++来实现。由上面的测试结果可以看到,通过C/C++扩展的方式实现斐波那 契数列计算,速度比Java还快。  如果单线程的Node不能满足需求,甚至用了C/C++扩展后还觉得不够,那么通过子进程的 方式,将一部分Node进程当做常驻服务进程用于计算,然后利用进程间的消息来传递结 果,将计算与I/O分离,这样还能充分利用多CPU。 CPU密集不可怕,如何合理调度是诀窍。 1.5.3 与遗留系统和平共处 有人会说:“JavaScript一统前后端了,将来会不会干掉其他的语言?”言语中充满了危机感。 在Web端,过去大多都是同步的方式编写的程序,这种串行调用下层应用数据的过程中充斥 着串行的等待时间,如果采用多线程来解决这种串行等待,又或多或少地显得小题大作。在Node 中,语言层面即可天然并行的特性在这种场景中显得十分有效。对于已有的稳定系统,并非意味 着我们要抛弃掉。 LinkedIn在他们的移动版网站上的实践非常典型地说明了这个问题。旧有的系统具有非常稳 定的数据输出,持续为传统网站服务,同时为移动版提供数据源,Node将该数据源当做数据接口, 发挥异步并行的优势,而不用关心它背后是用什么语言实现的。 这方面,国内的雪球财经也有很好的实践。雪球财经是从旧有的Java项目中分离出一个子项 目,在这个子项目中,没有继续采用Java/JSP而是采用Node来完成Web端的开发,使得前端工程 师在HTTP协议栈的两端能够高效灵活地开发,避免了Java烦琐的表达;另一方面,又利用Java 作为后端接口和中间件,使其具有良好的稳定性。两者互相结合,取长补短。 1.5.4 分布式应用 阿里巴巴的数据平台对Node的分布式应用算是一个典型的例子。分布式应用意味着对可伸缩 性的要求非常高。数据平台通常要在一个数据库集群中去寻找需要的数据。阿里巴巴开发了中间 层应用NodeFox、ITier,将数据库集群做了划分和映射,查询调用依旧是针对单张表进行SQL查 询,中间层分解查询SQL,并行地去多台数据库中获取数据并合并。NodeFox能实现对多台MySQL 数据库的查询,如同查询一台MySQL一样,而ITier更强大,查询多个数据库如同查询单个数据 库一样,这里的多个数据库是指不同的数据库,如MySQL或其他的数据库。 这个案例其实也是高效利用并行I/O的例子。Node高效利用并行I/O的过程,也是高效使用数 据库的过程。对于Node,这个行为只是一次普通的I/O。对于数据库而言,却是一次复杂的计算, 所以也是进而充分压榨硬件资源的过程。 1.6 Node 的使用者 在短短四年多的时间里,Node变得非常热门,使用者也非常多。这些使用者对于Node的各 自倚重点也各不相同。经过整理,主要有下面几类。 1.7 参考资源 11 1 2 3 4 5 11 6 7 8 9 10  前后端编程语言环境统一。这类倚重点的代表是雅虎。雅虎开放了Cocktail框架,利用自 己深厚的前端沉淀,将YUI3这个前端框架的能力借助Node延伸到服务器端,使得使用者 摆脱了日常工作中一边写JavaScript一边写PHP所带来的上下文交换负担。  Node带来的高性能I/O用于实时应用。Vox er将Node应用在实时语音上。国内腾讯的朋友 网将Node应用在长连接中,以提供实时功能,花瓣网、蘑菇街等公司通过socket.io实现实 时通知的功能。  并行I/O使得使用者可以更高效地利用分布式环境。阿里巴巴和eBay是这方面的典型。阿 里巴巴的NodeFox和eBay的ql.io都是借用Node并行I/O的能力,更高效地使用已有的数据。  并行I/O,有效利用稳定接口提升Web渲染能力。雪球财经和LinkedIn的移动版网站均是 这种案例,撇弃同步等待式的顺序请求,大胆采用并行I/O,加速数据的获取进而提升Web 的渲染速度。  云计算平台提供Node支持。微软将Node引入Azure的开发中,阿里云、百度均纷纷在云 服务器上提供Node应用托管服务,Joyent更是云计算中提供Node支持的代表。这类平台 看重JavaScript带来的开发上的优势,以及低资源占用、高性能的特点。  游戏开发领域。游戏领域对实时和并发有很高的要求,网易开源了pomelo实时框架,可 以应用在游戏和高实时应用中。  工具类应用。过去依赖Java或其他语言构建的前端工具类应用,纷纷被一些前端工程师用 Node重写,用前端熟悉的语言为前端构建熟悉的工具。 1.7 参考资源 本章参考的资源如下:  http://www.infoq.com/cn/articles/what-is-nodejs  https://github.com/popular/watched  http://groups.google.com/group/nodejs/browse_thread/thread/85f6a3829bc64cb6  http://groups.google.com/groups/profile?enc_user=dPo6jggAAACthftLMWCfUq8U6obMz179  http://search.npmjs.org/  http://code.google.com/p/v8/  http://cnodejs.org/topic/4f16442ccae1f4aa27001137  http://weibo.com/1744667943/eBszJXcEsX1  http://stackoverflow.com/questions/5621812/why-is-node-js-named-node-js  http://www.theregister.co.uk/2011/03/01/the_rise_and_rise_of_node_dot_js/page4.html  http://ued.taobao.com/blog/2011/09/02/what-is-nod/  http://www.infoq.com/cn/news/2012/04/interview-xueqiu-using-nodejs  http://teddziuba.com/2011/10/node-js-is-cancer.html  http://www.cnblogs.com/fengmk2/archive/2011/12/14/2288147.html
还剩27页未读

继续阅读

下载pdf到电脑,查找使用更方便

pdf的实际排版效果,会与网站的显示效果略有不同!!

需要 5 金币 [ 分享pdf获得金币 ] 2 人已下载

下载pdf

pdf贡献者

47959633

贡献于2014-06-05

下载需要 5 金币 [金币充值 ]
亲,您也可以通过 分享原创pdf 来获得金币奖励!
下载pdf