深刻浅出Node.js(一):什么是Node.js(转贴)

如下内容转自:http://www.infoq.com/cn/articles/what-is-nodejs/ 做者:崔康javascript

 

【编者按】:Node.js从2009年诞生至今,已经发展了两年有余,其成长的速度有目共睹。从在github的访问量超过Rails,到去年末Node.jsS创始人Ryan Dalh加盟Joyent得到企业资助,再到今年发布Windows移植版本,Node.js的前景得到了技术社区的确定。InfoQ一直在关注Node.js的发展,在今年的两次Qcon大会(北京站和杭州站)都有专门的讲座。为了更好地促进Node.js在国内的技术推广,咱们决定开设“深刻浅出Node.js”专栏,邀请来自Node.js领域的布道师、开发人员、技术专家来说述Node.js的各方面内容,让读者对Node.js有更深刻的了解,而且可以积极投入到新技术的讨论和实践中。前端


专栏的第一篇文章《什么是Node.js》尝试从各个角度来阐述Node.js的基本概念、发展历史、优点等,对该领域不熟悉的开发人员能够经过本文了解Node.js的一些基础知识。java

从名字提及

有关Node.js的技术报道愈来愈多,Node.js的写法也是五花八门,有写成NodeJS的,有写成Nodejs的,到底哪种写法最标准呢,咱们不妨遵循官方的说法。在Node.js的官方网站上,一直将其项目称之为”Node“或者”Node.js“,没有发现其余的说法,”Node“用的最多,考虑到Node这个单词的意思和用途太普遍,容易让开发人员误解,咱们采用了第二种称呼——”Node.js“,js的后缀点出了Node项目的本意,其余的名称五花八门,没有确切的出处,咱们不推荐使用。node

Node.js不是JS应用、而是JS运行平台

 

看到Node.js这个名字,初学者可能会误觉得这是一个Javascript应用,事实上,Node.js采用C++语言编写而成,是一个Javascript的运行环境。为何采用C++语言呢?据Node.js创始人Ryan Dahl回忆,他最初但愿采用Ruby来写Node.js,可是后来发现Ruby虚拟机的性能不能知足他的要求,后来他尝试采用V8引擎,因此选择了C++语言。既然不是Javascript应用,为什么叫.js呢?由于Node.js是一个Javascript的运行环境。提到Javascript,你们首先想到的是平常使用的浏览器,现代浏览器包含了各类组件,包括渲染引擎、Javascript引擎等,其中Javascript引擎负责解释执行网页中的Javascript代码。做为Web前端最重要的语言之一,Javascript一直是前端工程师的专利。不过,Node.js是一个后端的Javascript运行环境(支持的系统包括*nux、Windows),这意味着你能够编写系统级或者服务器端的Javascript代码,交给Node.js来解释执行,简单的命令相似于:nginx

#node helloworld.js

Node.js采用了Google Chrome浏览器的V8引擎,性能很好,同时还提供了不少系统级的API,如文件操做、网络编程等。浏览器端的Javascript代码在运行时会受到各类安全性的限制,对客户系统的操做有限。相比之下,Node.js则是一个全面的后台运行时,为Javascript提供了其余语言可以实现的许多功能。git

Node.js采用事件驱动、异步编程,为网络服务而设计

事件驱动这个词并不陌生,在某些传统语言的网络编程中,咱们会用到回调函数,好比当socket资源达到某种状态时,注册的回调函数就会执行。Node.js的设计思想中以事件驱动为核心,它提供的绝大多数API都是基于事件的、异步的风格。以Net模块为例,其中的net.Socket对象就有如下事件:connect、data、end、timeout、drain、error、close等,使用Node.js的开发人员须要根据本身的业务逻辑注册相应的回调函数。这些回调函数都是异步执行的,这意味着虽然在代码结构中,这些函数看似是依次注册的,可是它们并不依赖于自身出现的顺序,而是等待相应的事件触发。事件驱动、异步编程的设计(感兴趣的读者能够查阅笔者的另外一篇文章《Node.js的异步编程风格》),重要的优点在于,充分利用了系统资源,执行代码无须阻塞等待某种操做完成,有限的资源能够用于其余的任务。此类设计很是适合于后端的网络服务编程,Node.js的目标也在于此。在服务器开发中,并发的请求处理是个大问题,阻塞式的函数会致使资源浪费和时间延迟。经过事件注册、异步函数,开发人员能够提升资源的利用率,性能也会改善。后端

从Node.js提供的支持模块中,咱们能够看到包括文件操做在内的许多函数都是异步执行的,这和传统语言存在区别,并且为了方便服务器开发,Node.js的网络模块特别多,包括HTTP、DNS、NET、UDP、HTTPS、TLS等,开发人员能够在此基础上快速构建Web服务器。以简单的helloworld.js为例:

var http = require('http');
http.createServer(function (req, res) {
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.end('Hello World\n');
}).listen(80, "127.0.0.1");

上面的代码搭建了一个简单的http服务器(运行示例部署在http://helloworld.cnodejs.net/中,读者能够访问),在本地监听80端口,对于任意的http请求,服务器都返回一个头部状态码为200、Content-Type'值为text/plain'的”Hello World“文字响应。从这个小例子中,咱们能够看出几点:

  • Node.js的网络编程比较便利,提供的模块(在这里是http)开放了容易上手的API接口,短短几行代码就能够构建服务器。
  • 体现了事件驱动、异步编程,在createServer函数的参数中指定了一个回调函数(采用Javascript的匿名函数实现),当有http请求发送过来时,Node.js就会调用该回调函数来处理请求并响应。固然,这个例子相对简单,没有太多的事件注册,在之后的文章中读者会看到更多的实际例子。

Node.js的特色

下面咱们来讲说Node.js的特色。事件驱动、异步编程的特色刚才已经详细说过了,这里再也不重复。

Node.js的性能不错。按照创始人Ryan Dahl的说法,性能是Node.js考虑的重要因素,选择C++和V8而不是Ruby或者其余的虚拟机也是基于性能的目的。Node.js在设计上也是比较大胆,它以单进程、单线程模式运行(很吃惊,对吧?这和Javascript的运行方式一致),事件驱动机制是Node.js经过内部单线程高效率地维护事件循环队列来实现的,没有多线程的资源占用和上下文切换,这意味着面对大规模的http请求,Node.js凭借事件驱动搞定一切,习惯了传统语言的网络服务开发人员可能对多线程并发和协做很是熟悉,可是面对Node.js,咱们须要接受和理解它的特色。由此咱们是否能够推测出这样的设计会致使负载的压力集中在CPU(事件循环处理?)而不是内存(还记得Java虚拟机抛出OutOfMemory异常的日子吗?),眼见为实,不如来看看淘宝共享数据平台团队对Node.js的性能测试

  • 物理机配置:RHEL 5.二、CPU 2.2GHz、内存4G
  • Node.js应用场景:MemCache代理,每次取100字节数据
  • 链接池大小:50
  • 并发用户数:100
  • 测试结果(socket模式):内存(30M)、QPS(16700)、CPU(95%)

从上面的结果,咱们能够看到在这样的测试场景下,qps可以达到16700次,内存仅占用30M(其中V8堆占用22M),CPU则达到95%,可能成为瓶颈。此外,还有很多实践者对Node.js作了性能分析,总的来讲,它的性能让人信服,也是受欢迎的重要缘由。既然Node.js采用单进程、单线程模式,那么在现在多核硬件流行的环境中,单核性能出色的Node.js如何利用多核CPU呢?创始人Ryan Dahl建议,运行多个Node.js进程,利用某些通讯机制来协调各项任务。目前,已经有很多第三方的Node.js多进程支持模块发布,专栏后面的文章会详细讲述Node.js在多核CPU下的编程。

Node.js的另外一个特色是它支持的编程语言是Javascript。关于动态语言和静态语言的优缺点比较在这里再也不展开讨论。只说三点:

var hostRequest = http.request(requestOptions,function(response) {
    var responseHTML ='';
    response.on('data', function (chunk) {
        responseHTML = responseHTML + chunk;
    });
    response.on('end',function(){
        console.log(responseHTML);
        // do something useful
   });
});

在上面的代码中,咱们须要在end事件中处理responseHTML变量,因为Javascript的闭包特性,咱们能够在两个回调函数以外定义responseHTML变量,而后在data事件对应的回调函数中不断修改其值,并最终在end事件中访问处理。

  1. Javascript做为前端工程师的主力语言,在技术社区中有至关的号召力。并且,随着Web技术的不断发展,特别是前端的重要性增长,很多前端工程师开始试水”后台应用“,在许多采用Node.js的企业中,工程师都表示由于习惯了Javascript,因此选择Node.js。
  2. Javascript的匿名函数和闭包特性很是适合事件驱动、异步编程,从helloworld例子中咱们能够看到回调函数采用了匿名函数的形式来实现,很方便。闭包的做用则更大,看下面的代码示例:
  3. Javascript在动态语言中性能较好,有开发人员对Javacript、Python、Ruby等动态语言作了性能分析,发现Javascript的性能要好于其余语言,再加上V8引擎也是同类的佼佼者,因此Node.js的性能也受益其中。

Node.js发展简史

2009年2月,Ryan Dahl在博客上宣布准备基于V8建立一个轻量级的Web服务器并提供一套库。

2009年5月,Ryan Dahl在GitHub上发布了最第一版本的部分Node.js包,随后几个月里,有人开始使用Node.js开发应用。

2009年11月和2010年4月,两届JSConf大会都安排了Node.js的讲座。

2010年年末,Node.js得到云计算服务商Joyent资助,创始人Ryan Dahl加入Joyent全职负责Node.js的发展。

2011年7月,Node.js在微软的支持下发布Windows版本。

Node.js应用案例

虽然Node.js诞生刚刚两年多,可是其发展势头逐渐赶超Ruby/Rails,咱们在这里列举了部分企业应用Node.js的案例,听听来自客户的声音。

在社交网站LinkedIn最新发布的移动应用中,NodeJS是该移动应用的后台基础。LinkedIn移动开发主管Kiran Prasad对媒体表示,其整个移动软件平台都由NodeJS构建而成:

LinkedIn内部使用了大量的技术,可是在移动服务器这一块,咱们彻底基于Node。

(使用它的缘由)第一,是由于其灵活性。第二,若是你了解Node,就会发现它最擅长的事情是与其余服务通讯。移动应用必须与咱们的平台API和数据库交互。咱们没有作太多数据分析。相比以前采用的Ruby on Rails技术,开发团队发现Node在性能方面提升不少。他们在每台物理机上跑了15个虚拟服务器(15个实例),其中4个实例便可处理双倍流量。容量评估基于负载测试的结果。

企业社会化服务网站Yammer则利用Node建立了针对其自身平台的跨域代理服务器,第三方的开发人员能够经过该服务器实现从自身域托管的Javascript代码与Yammer平台API的AJAX通讯。Yammer平台技术主管Jim Patterson对Node的优势和缺点提出了本身的见解

(优势)由于Node是基于事件驱动和无阻塞的,因此很是适合处理并发请求,所以构建在Node上的代理服务器相比其余技术实现(如Ruby)的服务器表现要好得多。此外,与Node代理服务器交互的客户端代码是由javascript语言编写的,所以客户端和服务器端都用同一种语言编写,这是很是美妙的事情。

(缺点)Node是一个相对新的开源项目,因此不太稳定,它老是一直在变,并且缺乏足够多的第三方库支持。看起来,就像是Ruby/Rails当年的样子。

知名项目托管网站GitHub也尝试了Node应用。该Node应用称为NodeLoad,是一个存档下载服务器(每当你下载某个存储分支的tarball或者zip文件时就会用到它)。GitHub以前的存档下载服务器采用Ruby编写。在旧系统中,下载存档的请求会建立一个Resque任务。该任务实际上在存档服务器上运行一个git archive命令,从某个文件服务器中取出数据。而后,初始的请求分配给你一个小型Ruby Sinatra应用等待该任务。它其实只是在检查memcache flag是否存在,而后再重定向到最终的下载地址上。旧系统运行大约3个Sinatra实例和3个Resque worker。GitHub的开发人员以为这是Node应用的好机会。Node基于事件驱动,相比Ruby的阻塞模型,Node可以更好地处理git存档。在编写新下载服务器过程当中,开发人员以为Node很是适合该功能,此外,他们还里利用了Node库socket.io来监控下载状态。

不只在国外,Node的优势也一样吸引了国内开发人员的注意,淘宝就实际应用了Node技术:

MyFOX 是一个数据处理中间件,负责从一个MySQL集群中提取数据、计算并输出统计结果。用户提交一段SQL语句,MyFOX根据该SQL命令的语义,生成各个数据库分片所须要执行的查询语句,并发送至各个分片,再将结果进行汇总和计算。 MyFOX的特色是CPU密集,无文件IO,并只处理只读数据。起初MyFOX使用PHP编写,但遇到许多问题。例如PHP是单线程的,MySQL又须要阻塞查询,所以很难并发请求数据,后来的解决方案是使用nginx和dirzzle,并基于HTTP协议实现接口,并经过curl_multi_get命 令进行请求。不过MyFOX项目组最终仍是决定使用Node.js来实现MyFOX。

选择Node.js有许多方面的缘由,好比考虑了兴趣及社区发展,同时也但愿能够提升并发能力,榨干CPU。例如,频繁地打开和关闭链接会让大量端口处于等待状态,当并发数量上去以后,时常会由于端口不够用(处于TIME_WAIT状态)而致使链接失败。以前每每是经过修改系统设置来减小等待时间以绕开这个错误,然而使用链接池即可以很好地解决这个问题。此外,之前MyFOX会在某些缓存失效的状况下出现十分密集的访问压力,使用 Node.js即可以共享查询状态,让某些请求“等待片刻”,以便系统从新填充缓存内容。

小结

本文简要介绍了Node.js的基本知识,包括概念、特色、历史、案例等等。做为一个仅仅2岁的平台,Node.js的发展势头有目共睹,愈来愈多的企业开始关注并尝试Node.js,先后端开发人员应该了解相关的内容。

做者的微信公众号“老崔瞎编”,关注IT趋势,承载前沿、深刻、有温度的内容。感兴趣的读者能够搜索ID:laocuixiabian,或者扫描下方二维码加关注。

参考文献

[1] http://nodejs.org/

[2] http://beakkon.com/geek/node.js/why-node.js-single-thread-event-loop-javascript

[3] http://www.tbdata.org/archives/1285

[4] http://www.infoq.com/interviews/node-ryan-dahl

[5] http://www.infoq.com/cn/news/2011/08/enterprise-nodejs

[6] http://www.infoq.com/cn/news/2010/11/nodejs-joyent

[7] http://www.infoq.com/cn/news/2011/06/node-exe

[8] http://nodenode.com/post/1176414531/node-js-a-short-history

[9] http://www.infoq.com/cn/news/2011/05/nodeparty-hangzhou

【编者按】:本专栏欢迎有志于宣传和推广Node.js的布道师、开发人员和技术专家投稿,有意者请经过邮件与本专栏主持人崔康(cuikang[at]infoq.com)联系。

相关文章
相关标签/搜索