虚拟研讨会:Node.js生态系统之框架、库、最佳实践
http://www.infoq.com/cn/articles/nodejs-frameworks
Node.js是一个服务器端框架,基于Google的V8 JavaScript引擎创建,旨在利用事件触发、非阻塞的I/ O帮助开发人员构建高度可伸缩的网络程序。 目前很多流行的第三方库和框架都使用了Node.js, InfoQ联系了其中几个的创建者, 与他们展开了一次虚拟研讨会。
Node.js的创建者Ryan Dahl在JSConf 2010上做了演讲,简单介绍了什么是Node.js(PDF) :
- 服务器端的JavaScript
- 基于Google的V8创建
- 事件触发、非阻塞的I/O。 类似于EventMachine或Twisted。
- CommonJS模块系统。
- 有8000行C/C++代码, 2000行Javascript代码,14个贡献者。
Node.js最近越来越受关注,Yahoo! Mail的首席工程师Peter Griess也提到, 他们公司正在研究如何在产品中使用Node.js。
目前在GitHub上,大约有三百个项目都与Node.js有关 ,InfoQ联系了其中几个最受欢迎项目的创建者, 与他们展开了此次讨论。
参与者包括:
- 来自Express的TJ Holowaychuk, Express是受Sinatra影响的Node.js Web开发框架。(译注: Sinatra是用Ruby编写的开源Web应用框架、 领域特定语言。)
- Socket.IO的Guillermo Rauch,Socket. IO是一个简单的HTTP套接字接口实现及服务器。
- 来自Geddy的Matthew Eernisse,Geddy是个Node. js的Web开发框架,类似于Merb、Rails、 Pylons、Django等框架。
- node-xmpp的Astro,node- xmpp是遵循XMPP协议的Node.js库。
- StackVM的Peteris Krumins和James Halliday,StackVM是一家初创公司, 正尝试用Node.js来简化虚拟机在Web上的使用。
InfoQ:能向大家简单介绍一下你们的项目么? 项目试图解决什么问题?又是如何去做的呢?
TJ(Express):正如你们所了解的, Express很大程度上受到了Sinatra的影响, 最初主要是想让开发人员在新的平台上自如使用新的语法。 不过我们在1.x版本做了很多改进, Express现在利用了流行的中间件框架Connect( 我是共同编写者),并移除了一些不适合项目的依赖。
有些Node框架只关注自身,但大部分还是想成为“最重要的” 库。我期望Express是可选的,不会带来什么局限。
Guillermo(Socket.IO):Socket. IO提供了一个简单的API, 可以抽象出各个浏览器在HTTP传输实现上的不同, 借此让实时JavaScript应用的构建成为可能。
Matthew(Geddy):Geddy是个全JavaScript堆栈的MVC Web框架。 Geddy想让开发人员很容易地用JavaScript编写复杂的Web应用、在客户端和服务器之间共享代码。
Geddy使用了现有MVC框架中的常见模式, 比如Rails和Django中用控制器/行为、模型、 模板化的函数。 不过Geddy中的这些功能都是用JavaScript完成的, 所以模型、验证、模板之类的内容也都可以在浏览器中使用。
Geddy有简单、基于资源的路由机制,还有准确的内容协商, 所以用它来开发传输结构化数据的轻量Web Service会超级简单。
Astro(node-xmpp):我的目标是创建一个能在Node.js环境中容易使用的XMPP库。 先前的库以适用于浏览器的Strophe.js为基础, 不符合Node.js的约定; 也没有利用EventEmitter和支持SRV的DNS解析器 ,最重要的是,它既不支持XMPP客户端,也不支持组件连接。
Peteris和James(StackVM):我们的项目叫StackVM, 它可以借助HTML和JavaScript让虚拟机在浏览器上运 行。StackVM显示了虚拟机不绑定到桌面时可以进行的操作。 比如说,用户可以在他们的网站里嵌入虚拟机、 与合作者共享工作内容、为应用创建在线的产品演示, 还可以创建以前不可能实现的Mashup。
InfoQ:你们项目里的主要模块是什么?它们怎样和其他模块、 Node.js进行交互?
TJ(Express):Connect中间件框架在Expre ss 1.x里发挥了重要作用,以前称为“插件” 的组件现在都是Connect“中间件”。 这种抽象意味着社区可以在更短的时间周期内借助所有通用的内容生成强大的框架。Express 1.0测试版的核心组件和功能有:
- 强大的路由
- 视图系统,包括对局部视图和集合视图的支持
- 基于环境的配置
- 基于会话的持久、迅速的通知
- 内容协商
- 响应工具
- 重定向工具
- 能快速生成应用骨架
Guillermo(Socket.IO):Socket. IO包括一个在浏览器上运行的JavaScript客户端, 还有一个在服务器(Socket.IO-node) 上运行的JavaScript组件。
Matthew(Geddy):Geddy是个MVC框架。 你可以在“控制器”上定义函数,把URL映射到动作上。 还可以创建“模型”,定义控制器中要使用的对象。最后, 你可以通过渲染“视图”对请求作出响应,“视图” 可以是某种模板,或者仅仅是格式化的数据。
Geddy用Node.js实现了它的HTTP服务器, 在服务器端执行所有的函数、控制器动作、 模板渲染时也使用了Node.js。 由于是全JavaScript堆栈, 很多相同的代码也是在浏览器中运行的。
Astro(node-xmpp):Node. js追求可伸缩的服务器软件性能,这点对我来说非常重要。 这就是为什么我在评估可用的XML SAX解析器库之后,自己创建了node-expat的原因。 Expat用C++编写, 和快速的解析器库libexpat进行了绑定。
我过去经常使用Ruby XMPP库xmpp4r,所以node- xmpp仿照了xmpp4r的一点儿内容。 客户端和组件都来自于连接,连接进行XML流处理。 连接基于普通的TCP客户端流, 所以用户仍然能访问原始数据管理、发送队列管理等功能。
Peteris和James(StackVM):我们的主要组成 部分就是StackVM本身(http://github. com/pkrumins/stackvm/), 它使用了我们为Node.js编写的几个其他模块:
- dnode(http://github.com/ substack/dnode) :DNode为远程方法调用提供了一个简单、面向对象的接口, 并借助透明包装的回调来传送数据, 从而简化了StackVM进程和Web浏览器之间的通信。
- node-bufferlist(http://github. com/substack/node-bufferlist): 使用bufferlist, 我们可以用异步方式构建一个二进制的流解析器。
- node-rfb(http://github.com/ substack/node-rfb):这个模块用node- bufferlist告诉客户端VNC使用的RFB协议。
- node-png(http://github.com/ pkrumins/node-png):node- rfb捕获到屏幕更新后,我们用该模块渲染PNG图片。
- node-jpeg(http://github.com/ pkrumins/node-jpeg): 这个模块使用node-rfb的输出, 生成JPEG格式的屏幕更新图片。
- node-video(http://github.com/ pkrumins/node-video):借助这一模块, 我们可以为node-rfb捕获到的屏幕更新保存截屏记录。
- node-base64(http://github.com/ pkrumins/node-base64): 该模块对node-png、node- jpeg的输出进行base64编码,并把结果发送给浏览器( 因为没有什么简单的方法可以对二进制数据进行流传输)。
接着我们用Socket.IO(http://github. com/LearnBoost/Socket.IO) 和别人编写的Socket.IO-node(http:// github.com/LearnBoost/Socket. IO-node)模块来完成浏览器和StackVM之间的通信。
StackVM的工作原理串起来就是: StackVM使用node-bufferlist和node- rfb打开一个到虚拟机VNC端口的连接。屏幕出现更新时, node-rfb将更新信息转发给node-png或node- jpeg,由它们生成图像。接着,图像由Socket. IO转发给浏览器。DNode对Socket. IO的传输进行了抽象, 用更加异步的方式在服务器和浏览器之间路由消息。
InfoQ:你们选用Node.js的主要原因是什么? 日常工作中会有哪些好处?
TJ(Express):大概在一年前, 当我开始做Express的时候我对Node产生了兴趣。 我希望用熟悉的语言、熟悉而简单的方式去定义Web应用。 我听说Node不久后就下载了源码、简单阅读了一下, 我喜欢它所引导的方向。用了几年Ruby之后, 我发现我越来越喜欢用JavaScript了。
Guillermo(Socket.IO):用Node编写实时应用真的非常简单。我们选择Node的主要原因是, 在Node里你是司机, 它能让你只用几行代码就创建出一个自己的HTTP服务器。 传统堆栈都没有这种能力,比如常见的Apache+PHP, 因为PHP只能在请求完成时执行,而且要运行一小段时间, 才能生成响应并退出。
另一方面,不用牺牲性能和可伸缩性, Node的请求就可以延长一段时间,因为Node是个完全异步、单线程、单进程的框架。
Matthew(Geddy):我选择Node.js是因为, 它是第一个可靠的服务器端JavaScript实现, 不以JVM为基础。 我在日常工作中编写了很多JavaScript代码, 一个很大的优点就是, 不需要针对客户端和服务器端在两种语言之间换来换去。
Astro(node-xmpp):对IO操作比较多的应用, 我想控制它们的并发问题。典型的选择是线程编程,但这很昂贵。 Erlang和GHC把轻量级的绿色线程映射到了本地线程上( 一个CPU内核有一个本地线程),以此来解决并发问题。 尽管有这一功能,开发人员仍要忍受同步问题、避免竞态条件。
异步编程时不用开发串行的“线程”代码,你会觉得这有些不自然。 但不处理那些麻烦的同步问题确实让我觉得轻松了很多。 一旦你理解了这个概念,就会习惯它带来的“阶梯效应”, 功能会逐层嵌套实现。
Peteris和James(StackVM):不用刻意展开或编写C代码,Node.js就有非常棒的性能。Node. js没有任何需要担心的遗留代码,所有内容自底向上都是异步的。 还有很多库在尝试一些新的事情,比如websockets, 社区也非常活跃。 在浏览器和服务器端使用相同的语言让代码重用变得更为简单, 而且JavaScript语言很简单,能迅速完成原型。
InfoQ:你觉得Node.js成为主流的时机成熟了么? 你怎么看它周围的生态系统呢?
TJ(Express):这个问题不太好回答, 和其他很多社区相比,Node.js仍然很小。 我个人觉得这个项目(和社区)有很大的潜力,而且Django、 Drupal或Ruby on Rails最终也很有可能会跟进,甚至做出更大的贡献。
Guillermo(Socket.IO):Node正在非常迅 速地成熟起来。在GitHub(http://github. com/ry/node)上, 它的追随者现在差不多是Rails(http:// github.com/rails/rails)追随者的一半, Rails无疑是最流行的Web应用开发框架之一。 而这只用了一段很短的时间。
Matthew(Geddy):对有些目标来说它已经很成熟了, 但人们要像用Ruby或Python那样去进行通用的开发还为时尚早。也许还需要一两年时间。
Astro(node-xmpp):Node. js有朝一日会很容易成为主流的, 因为JavaScript是个相当流行的语言。
Node.js一直在发展,你应该经常针对新版本测试你的代码。 如果你打算让任何人都能运行你的代码,却不管用户的Node. js版本到底是新还是旧,我觉得这会有问题。
Peteris和James(StackVM):它要是在某个方面还不够成熟,我们会让它更加成熟的。它是开源的, 所以我们要是发现一个Bug、或是需要一些新特性, 必要的话我们会自己进行处理。社区驱动的生态系统非常棒:开源、 很多想法、大量参与其中的开发人员、一大堆文档和博客文章, 还有GitHub上每天都会有更新的Node.js库。
API在不同的版本之间确实会略有变化,但Ryan Dahl承诺,即将发布的0.2版本会具备全新的稳定性。
InfoQ:你在项目或日常工作中使用其他的Node. js库或框架么?除了你自己的库,你觉得有没有其他的Node. js库或框架是特别有用、别的开发人员也应该去看一看的?
TJ(Express):日常工作中我用了很多自己的库, 还有下面几个库, 我对这几个项目和它们的开发人员怀有深深的敬意:
- Socket-IO http://github.com/LearnBoost/ Socket.IO (Guillermo Rauch)
- Formidable http://github.com/felixge/ node-formidable (Felixge)
- Less.js http://github.com/cloudhead/ less.js (Cloudhead)
- Redis客户端 http://github.com/fictorial/ redis-node-client (Fictorial)
Guillermo(Socket.IO):我们天天都用Node。除了Socket.IO,我支持Mongoose(http ://github.com/learnboost/ mongoose)和其他一些较小的组件, Mongoose是一个ORM框架,针对NoSQL、 面向文档的数据库MongoDB。
Matthew(Geddy):Node-Jake是个类似于Make/ Rake的JavaScript构建工具:http://github.com/mde/node- jake
Logan是个小型的跨环境测试框架, 可以用它在浏览器里测试只能运行在Node.js中的代码( 还有TheRubyRacer, 它把V8嵌套在一个Ruby进程中):http://github.com/mde/logan
Astro(node-xmpp):有几个需要提一下。 第一个是Isaac Schlueter开发的包管理器npm。无论什么地方, 你都想拥有包管理器所带来的便利。
TJ Holowaychuk开发的ext.js相当有用: 它用很多方便的方法对基本的JavaScript类型进行了增强 ,所有人都会发现这很有用。
不要错过Senchalab的Connect Web框架。Node.js内置的HTTP支持是它的杀手锏, Connect想要达到Rack在Ruby世界中的水平。当然, 这并不像看起来的那么简单,因为它完全是异步的。
Peteris和James(StackVM):借助Node包管理器npm(http://npmjs.org/)去使用、 发现其他开发人员编写的库是一种非常好的方式。 我们使用Socket.IO, 它对WebSocket进行了抽象, 还对尚没有WebSocket的浏览器提供了应变方案, 以支持WebSocket式的通信。 我们也一直在尝试基于Socket.IO的node- compress,以便加快把数据传输到浏览器的速度。
InfoQ:如果一个团队要开始使用Node.js, 他们需要留心哪些陷阱?有提示或“最佳实践”么?
TJ(Express):一般来说都有环境问题。 我认为Git是必需的,其他SCM都让我觉得不太满意, 实际是有点儿反感。搭建一个持续集成的服务器, 哪怕像CIJoe(http://github.com/ defunkt/cijoe)那么简单,那也是必需的; 还有就是测试、测试、再测试!我着重强调测试, 我所有的Node项目都用“Expresso”(http:// github.com/visionmedia/ expresso)进行测试,“Expresso” 项目支持代码覆盖率报告, 能让你很明确地知道哪些代码没有覆盖到, 这些简单的步骤应该能让项目不停向前滚动。
Guillermo(Socket.IO):我的建议是:
- 对JavaScript语言要有非常强烈、近乎狂热的求知欲。
- 深刻理解保证网站运转的各个层面。这意味着要理解底层的知识( 熟知HTTP、协议和网络通常会有较大的帮助)。
- 对于某些功能,试着“忘记”其他语言实现它们的方式,学习用“ Node”的方式去实现它们( 比如在Java里用线程去解决的问题, 你可以用Node以不同的方法去轻松解决)
Matthew(Geddy):Node. js仍然处于发展初期,所以仍然会有API的变化, 这会破坏代码。要准备好修改代码, 或者用Geddy或Express等框架, 框架的作者会屏蔽Node的变化,保证API的一致性。
Astro(node-xmpp):尽管JavaScript的语法微不足道,看过用大括号划分程序代码的任何人都能看懂, 但基于原型的遗留系统一开始还是会造成混乱。 一定要告诉你的伙伴会有什么样的影响。
要是你仍然觉得基于类的遗留系统更容易使用, 那你可能需要找一个处理方式类似于用JavaScript处理遗留Ruby的库。
Peteris和James(StackVM):阻塞I/O可能是需要留意的最大陷阱, 因为整个程序等到调用完成之后才会执行其他操作。 只在初始化时使用少量的阻塞调用, 尤其是在编写注重性能的网络服务时。异步I/ O和事件驱动编程需要的思维方式和大多数编程都不太相同。 拥抱它吧。
InfoQ:根据你的经验, JavaScript代码库规模如何像项目那样变得越来越大? 你用的工具有哪些?你觉得工具对现实中的Node. js项目支持得怎样?
TJ(Express):我觉得这完全由开发人员来决定。 JavaScript过去曾是一门“丑陋”的语言, 但现在已经不是了。我觉得行内注释非常重要, 有助于创建可见的空白,而且不会让代码看起来混乱不堪。 至于整个项目的组织,把逻辑块分布到单独文件中绝不会有错, 对任何语言来说这都是通用的做法。
Node采用了CommonJS的模块模式, 这其实是我在语言中使用过的最喜欢的组织工具。最典型的,大多数环境都包含一个扁平的名字空间, 所以在现实中你仍然要随时处理名字空间的冲突, 而CommonJS的模块系统就很好地避免了冲突。
Guillermo(Socket.IO):让JavaScript同时在服务器、客户端上运行是很让人兴奋的。 随着招聘变得越来越容易, 整个团队可以处理同一应用的很多不同方面, 我觉得这对公司来说特别重要。 Node也为专业环境中的开发提供了很多工具:
- 简单却强大的模块系统(基于CommonJS)
- 很多测试框架(Expresso、Vows等)
Matthew(Geddy):在服务器端, JavaScript现在和Ruby、Python一样, 已经有很多工具了—— CommonJS模块提供了让代码保持模块化的机制。Node- Jake等构建工具能让你的构建和打包自动化, 以可预测的方式进行。
客户端的JavaScript还需要再多做一点儿工作。 但JavaScript对象能很自然地封装功能, 而且JavaScript很久以前就有了在客户端进行“ 大规模编程”的最佳实践。 Facebook等大公司创建了他们自己的框架, 但Dojo或YUI等更复杂的工具包则需要更多这样的支持。
Astro(node-xmpp):我推荐在大型项目中使用静态类型。
我也很喜欢测试驱动开发;不知道Node. js社区是否已经选择了测试库的事实标准。
Peteris和James(StackVM):Node. js有一个模块系统,它对项目的可伸缩性大有裨益, 但大型软件项目不论用什么语言,一般都有更多的Bug, 而且比小型项目更难测试。对于StackVM, 我们将功能分解为可重用、开源的模块, 以此来尽可能长久地避免大型代码库的这种缺陷。到目前为止, 这种方法让StackVM的主要代码库更为灵活, 对支持库进行单独测试也更容易。 社区里使用我们模块的人已经为我们提出了很多Bug。 现在对Node. js来说最有用的工具也许是Node包管理器npm。 使用npm,我们能轻松使用其他程序员编写的库, 我们也可以很容易地和Node.js社区分享我们自己的模块。
InfoQ:你觉得Node.js现在还缺些什么、 需要添加进来呢?你认为它应该如何发展?
TJ(Express):我觉得应该继续保持原样, 为框架做越来越有影响的底层基础。它应该从“空白”开始, 不受开发人员的影响。
Guillermo(Socket.IO):HTTP服务器对SSL的支持还不完整,还有很多Bug。除此之外, 它最终是要让人们去用的,要考虑通用用法、修复Bug, 继而实现最佳模式。
Matthew(Geddy):大家都在使用包管理器(NPM) ,所以希望在NPM稳定之后,我们能以一种简单的方式安装库、 为构建的项目指定依赖关系。
很多刚起步的项目都在尝试处理数据存储和持久化, 但还没有最佳解决方案。我们仍需要一个稳定的ORM, 它能为关系型存储和非关系型存储同时提供通用的API。
服务器端JavaScript还有很多尚未开始开发的需求, 所以我觉得生态系统会很自然地转去填补目前的这些空白。
Astro(node-xmpp):我特别想从node- xmpp中删除对node-base64的依赖。 暴露Base64编码/解码的补丁已经发布,但还没有合并进来。
我发现到处都在提CommonJS。 这个项目旨在对跨多个JavaScript接口/ 框架的库进行标准化。他们已经实现了模块系统, NPM也使用他们的package.json规范。 除CommonJS之外,恐怕不会有太多的共享模式, 因为相对很多其他的实现来说,Node. js里的所有内容都必须是异步的。
对很多人来说,找到合适的Web框架也很有意思。
异步编程有时候非常麻烦,我明白开发人员有时候忽略回调引用, 是否只是想让控制流能更加直截了当一些。
Peteris和James(StackVM):每当我们想出一 些有潜力的Node.js新库,我们会自己开发并发布出来, 这样其他开发人员就不用浪费时间去重新发明轮子了。 其他很多程序员也都在这么做, 库的支持生态系统就会很自然地演进。Node. js核心就是要尽可能小,由支持库来提供大量的功能, 对社区驱动的项目来说,这是一个分配工作负担、 鼓励参与的很好的方式,
InfoQ:你项目以后的路线图是怎样的?
TJ(Express):现在还是让Express保持精简。 对于那些适合项目的重点功能,我都会接受它们的新思路和补丁。 开发人员现在甚至可以把Express看成是“框架的框架”, 因为对那些更大型、偏向于自己使用的框架来说, 它本身就很容易成为构建块。 最后我要感谢Ciaran和Aaron的巨大贡献, 还有我的老板Sencha允许我和Tim回馈社区。
Guillermo(Socket.IO):我现在正努力让So cket.IO尽可能地迅速、精简、易于测试。
Matthew(Geddy):Geddy还有很多工作要做:
- ORM查询API
目前模型代码中的持久化部分是非常重要的。ORM非常复杂, 任何既要和关系型存储交互, 又要和非关系型存储交互的内容都越来越难构建。
- 插件API
Geddy需要一种简单的方式,去支持第三方构建的插件功能。
- 认证
认证就是应该用插件去实现的绝好例子。
- 更好的模块化
编写Geddy是为了提供一种集成的、“开箱即用”的体验, 让开发人员编写真实应用时不那么手忙脚乱。 但Geddy提供组件应该更容易选择和使用, 或者某个特定的部分能很容易地被其他内容替换。
- 除了简单的EJS,还要支持其他的模板库
人们喜欢用奇妙的东西,比如Haml或Moustache。 Geddy应该让大家能使用这些内容。
- 测试覆盖率
Geddy有大量针对路由器的测试, 还有一套针对模型和验证的测试套件,但其余代码还没有覆盖到。 Geddy的代码需要进行重构、要更容易测试, 以便我们有更完整的覆盖率。
Astro(node-xmpp):我很高兴在发布项目之后, Github上马上就有了关注者。除此之外, 我一直在期待一些实质性的贡献。
我自己的目标已经达到了,因为我只想完成XML解析和认证, 并不是在库的基础上实现所有特定的XMPP XEP规范。XMPP XEP规范的实现就留给应用开发人员去完成吧。
Peteris和James(StackVM):短期内, 我们会侧重于添加更多的功能,还有性能优化。 以后我们很可能会关注硬件和可伸缩性。
在InfoQ上,你可以找到更多关于性能和可伸缩性、或Java Script的内容!
查看英文原文:Virtual Panel: The Node.js Ecosystem - Frameworks, Libraries and Best Practices
感谢崔康对本文的审校。
给InfoQ中文站投稿或者参与内容翻译工作,请邮件至editors@cn.infoq.com。也欢迎大家加入到InfoQ中文站用户讨论组中与我们的编辑和其他读者朋友交流。