如何提高架构的稳定性、可扩展性和易用性等能力?点击看大咖分享
写点什么

深入浅出node.js(一):什么是node.js-金马国际

  • 2011 年 10 月 13 日
  • 本文字数:5979 字

    阅读完需:约 20 分钟

【编者按】:从 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 的一些基础知识。

从名字说起

有关 node.js 的技术报道越来越多,node.js 的写法也是五花八门,有写成 nodejs 的,有写成 nodejs 的,到底哪一种写法最标准呢,我们不妨遵循官方的说法。在 node.js 的上,一直将其项目称之为”node“或者”node.js“,没有发现其他的说法,”node“用的最多,考虑到node 这个单词的意思和用途太广泛,容易让开发人员误解,我们采用了第二种称呼——”node.js“,js 的后缀点出了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 来解释执行,简单的命令类似于:

复制代码
#node helloworld.js

node.js 采用了 google chrome 浏览器的 v8 引擎,性能很好,同时还提供了很多系统级的 api,如文件操作、网络编程等。浏览器端的 javascript 代码在运行时会受到各种安全性的限制,对客户系统的操作有限。相比之下,node.js 则是一个全面的后台运行时,为 javascript 提供了其他语言能够实现的许多功能。

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

事件驱动这个词并不陌生,在某些传统语言的网络编程中,我们会用到回调函数,比如当 socket 资源达到某种状态时,注册的回调函数就会执行。node.js 的设计思想中以事件驱动为核心,它提供的绝大多数 api 都是基于事件的、异步的风格。以 net 模块为例,其中的 net.socket 对象就有以下事件:connect、data、end、timeout、drain、error、close 等,使用 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 服务器(运行示例部署在中,读者可以访问),在本地监听 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.2、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]

[2]

[3]

[4]

[5]

[6]

[7]

[8]

[9]

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


感谢对本文的审校。

给infoq 中文站投稿或者参与内容翻译工作,请邮件至。也欢迎大家加入到中与我们的编辑和其他读者朋友交流。

2011 年 10 月 13 日 00:00245900

发布了 501 篇内容, 共 227.6 次阅读, 收获喜欢 51 次。

关注

评论

发布
暂无评论
  • 2011年,parse借助ruby on rails快速推出了第一个版本。但随着代码库的增长,部署时间越来越长,“一个请求一个进程”的rails模型开始无法满足平台的扩展需求。于是,在评估了eventmachine、jruby、c 、c#、go等可选方案后,他们选择了go。

  • 是一个巴西人设计的小巧的脚本语言,它的设计目的是为了能够嵌入到应用程序中,从而为应用程序提供灵活的扩展和定制功能。

  • 在 ruby 3.0 发布后,松本行弘接受了一次专访。在专访中,他介绍了 ruby 的最新特性以及他改进 ruby 的方法,并分享了他对 ruby 未来的见解。

  • 随着node.js逐渐受到社区的关注,开发人员都拿它与rails作比较,有人说node.js会替代rails,有人说rails优势明显,一直存在争论,我们来看一下双方的擅长领域。

  • 这是整个 rack 系列文章的最后一篇了

  • 在经过几年时间的开发之后,suave 1.0终于于近期正式发布了。infoq与suave的维护者,同时也是qvitoo的ceo henrik feldt进行了一次访谈,以深入地了解这个框架的功能以及它的开发历史。

  • 本文为你讲解了如何实现一套最简单的 webrtc 信令服务系统。有了它,你就能真正地体验到 webrtc 的强大之处了。

    2019 年 8 月 8 日

  • 2018 年 12 月 18 日

  • shelljs给node.js添加了一个编写shell脚本的用处,但node.js的异步i/o对shell脚本来说是块难啃的骨头。shelljs虽然找到了办法,但效率低下又丑陋不堪。所以node.js的核心团队决定让node v0.12支持同步运行子进程。

  • 这节我要讲几种常见的编写服务器端的语言,以及它们各自优缺点,以及究竟如何去选择合适的编程语言去编写网络服务。

    2018 年 7 月 31 日

  • 都说 rust 好,但是就是入门难,所以它真的值得你花精力学习吗?

    2021 年 8 月 24 日

  • sails是一个构建于node.js之上的实时mvc框架。位于得克萨斯州奥斯汀的balderdash团队在4月9日发布了sails 0.8.9版。balderdash团队长期并持续地致力于为现代web应用打造类rails的开发平台。

  • 被称为node之父的ryan dahl在2012年离开了node项目,后来加入google的brain团队,从事深度学习方面的研究,主要专注在图像的着色和超解像技术上。拥有深厚数学功底的ryan经历了从web开发到深度学习的跨越,还主导了多个开源项目,如http解析器、libuv等,可以说是一个技术多面手。他不喜欢被人定义成某个领域的专家。mtj(mapping the journey)网站对ryan进行了一次深度访谈,他在访谈中提及了他的成长经历、node的开发始末、个人的职业变迁以及对深度学习和人工智能的看法。

  • node作为服务器端的javascript运行环境,帮助更多的人将技术延伸到后端系统。它具有无阻塞、事件驱动等特点,让熟悉javascript的开发者无需学习java、c 等传统“后台”语言就可快速简便地创建服务器端应用。不过,初出茅庐的node,其成熟和健壮程度是否足够应用于企业级生产环境中,一直是技术社区关注的热点。越来越多的企业开发者在实际应用中采纳了node相关技术,并给予了肯定,这些企业包括linkedin、yammer、github、淘宝等,他们的实践和反馈值得技术社区参考。

  • 2020 年 10 月 15 日

  • infoq中文站报道过一些公司从ruby转移到其他语言的新闻,包括"iron.io从ruby迁移到go:减少了28台服务器并避免了连锁故障"、"从ruby向java的迁移帮助twitter挺过了美国大选"和"在linkedin的ruby on rails和node.js对决"。最近,it元老范凯在博客中详细地分析了rails目前在web服务方面的局限和原因,针对“继续沿用ruby on rails重写或者重构应用,性能可能会有一两倍的提升,但无法弥合10倍以上的性能差距,难道说ruby真的如此不堪吗?注定要被node.js或者go所取代吗?”这个问题给出了自己的观点,即:使用其他ruby轻量级框架取代rails,而不是使用node.js和go取代ruby。

  • node.js是一个服务器端框架,基于google的v8 javascript引擎创建,旨在利用事件触发、非阻塞的i/ o帮助开发人员构建高度可伸缩的网络程序。 目前很多流行的第三方库和框架都使用了node.js, infoq联系了其中几个的创建者, 与他们展开了一次虚拟研讨会。

  • 继postgres之后,neverblock现在也通过新的mysqlplus适配器为mysql提供了支持。我们采访了两位mysqlplus的开发者,并与ruby的oracle和sqlite接口开发者 讨论了neverblock以及无阻塞数据库适配器。

  • 2020 年 7 月 1 日

发现更多内容

大规模 kubernetes 集群运维实践

大规模 kubernetes 集群运维实践

网站地图