1.1 Node.js基础
本节包括Node.js的基础简介、发展历史、组织架构,以及具体应用等方面的内容。
1.1.1 Node.js简介
Node.js是基于Google Chrome浏览器内置的V8引擎所开发的JavaScript运行时环境。它充分利用了V8引擎的强大性能,借鉴了其中的很多前沿技术(例如:GC机制、事件驱动、非阻塞的I/O模型,等等),保证了Node.js的轻量与高效,进而受到了众多开发者的追捧。
Node.js最显著的特点就是能够运行在服务器端(区别于其他脚本语言),以及良好的多平台兼容性(支持Windows、Linux、Mac OS X、SunOS和FreeBSD等多种系统平台),使其成为最重要的脚本程序设计语言。
我们都知道,一般的JavaScript脚本语言需要在浏览器环境下才可以解释执行。而Node.js是服务器端的脚本语言,可以直接在后端解释执行。下面就是最基本的Node.js命令执行方法。
#node filename.js // TODO: node命令直接解释执行filename.js脚本文件
由于Chrome V8引擎执行JavaScript脚本的速度非常快,因此Node.js所开发出来的应用程序性能非常好。Node.js已经成为全栈开发的首选语言之一,并且从它衍生出众多出色的全栈开发框架。Node.js已经在全球被众多公司使用,包括创业公司Voxer、Uber以及沃尔玛、微软这样的知名公司。它们每天通过Node.js处理的请求数以亿计,可以说在要求苛刻的服务器系统,Node.js也可以轻松胜任。
Node.js还包括一个完善的社区。在Node.js的官方网站http://nodejs.org/可以找到大量的文档和示例程序,并且Node.js还有一个强大的NPM包管理器。渐渐地,越来越多的人参与到本项目中来,可用的第三方模块和扩展增长迅猛,而且质量也在不断提升,Node已是全球较大的开源库生态系统之一。
提示
Node.js并不是一个JavaScript应用,而是一个JavaScript的运行时环境,其底层由C++语言编写而成。
1.1.2 Node.js发展历史
任何语言或框架都不是一天形成的,而是经过漫长的测试、发布、再测试、再发布的迭代过程,本节重点介绍一下Node.js的发展历史。
Node.js的创始人就是大名鼎鼎的Ryan Dahl,其实Ryan Dahl本人是数学专业的。在2008年年末,一个偶然的机会让他了解到Google推出了一个全新的Chrome浏览器及其V8引擎。而当他了解到,Chrome V8是一个为了实现更快的Web体验而专门制作的JavaScript引擎时,非常希望找到一种语言能提供先进的推送功能并集成到自己的网站中去,从而避免采用传统的不断轮询拉取数据的方式。
Ryan Dahl对C/C++和系统调用非常熟悉,他使用系统调用(用C)实现消息推送这样的功能。如果只使用非阻塞式Socket,每个连接的开销都会非常小。在小规模测试中,它能同时处理几千个闲置连接,并可以实现相当大的吞吐量。但是,他并不想使用C,他希望能采用另外一种漂亮灵活的动态语言。他最初也希望采用Ruby来写Node.js,但是发现Ruby虚拟机的性能不能满足要求,后来便尝试采用V8引擎,所以选择了C++语言。
2009年2月,Ryan Dahl首次在自己的博客上宣布准备基于V8创建一个轻量级的Web服务器并提供一套库,2009年5月他正式在GitHub上发布最初版本的部分Node.js包。随后几个月里,有人开始使用Node.js开发应用。实践证明,JavaScript与非阻塞Socket配合得相当完美,只需要简单的几行JavaScript代码就可以构建出非常复杂的非阻塞服务器。时间到了2010年年底,Node.js获得云计算服务商Joyent的资助。创始人Ryan Dahl加入Joyent,全职负责Node.js的发展。Node.js从此以后迅猛发展,并成为一种流行的开发语言。
在官方网站上Node.js的版本号是从0.1.14开始的,每个发布版本对应不同的V8引擎版本和NPM包管理器版本,截至作者写作时最新的LTS版本为V12.16.3。当然,这期间Node.js发生了很曲折的故事,感兴趣的读者可以自行去了解一下。
总结一下,Node.js的发展大致可以分为如下4个阶段。
1.发展初期
创始人Ryan Dahl带着他的团队开发出以Web为中心的Web.js,此时的一切都非常混乱,API也大多都处于试验阶段。
2.快速发展时期
Node.js的核心用户Isaac Z. Schlueter开发出奠定了Node.js如今地位的重要工具——NPM包管理工具。同时,这也是Schlueter未来成为Ryan接班人的重要条件。之后Connect、Express、Socket.io等库的出现吸引了一大批爱好者加入到Node.js开发者的阵营中来。CoffeeScript的出现更是让不少Ruby和Python开发者找到了学习的理由。其间一大波以Node.js作为运行环境的CLI工具涌现,其中不乏用于加速前端开发的优秀工具,如Babel、Less、Sass、UglifyJS、Browserify、Grunt、Gulp等。在这个阶段,Node.js的发展势如破竹。
3.不稳定时期
经过了一大批一线工程师的探索实践后,Node.js开始进入时代的更迭期,新模式代替旧模式,新技术代替旧技术,新实践代替旧实践。ECMAScript 6(ECMAScript 2015)也开始出现在Node.js世界中。ECMAScript 6的发展越来越明显,V8也对ECMAScript 6中的部分特性实现了支持,如Generator等。
4.稳步发展时期
随着ECMAScript 6的发展和最终定稿,出现了一大批利用ECMAScript 6特性开发的新模块,如原Express核心团队所开发的Koa。Node.js之父Ryan Dahl退出Node.js的核心开发,转而做其他的研究项目。Ryan Dahl的接任者Isaac Schlueter负责将Node.js一直开发下去并进行不断完善。
1.1.3 Node.js组织架构
前面介绍了Node.js是一个完整的JavaScript开发环境,是基于Google的Chrome V8引擎对代码进行解释执行。它在设计之初就已经定位用来解决传统Web开发语言所遇到的诸多问题,所以Node.js有很多其他开发语言所不具备的优点。下面介绍Node.js的组织架构。
从图1.1中可以看到,只有最顶层的Node标准库部分是用JavaScript语言编写的,其余的底层部分均是用C/C++语言编写的。
图1.1 Node.js组织架构图
我们继续分析图1.1中描述的Node.js组织架构,关于Node.js的结构大致可分为以下三个层次。
1.Node.js标准库(Node standard library)
这一层由JavaScript编写,是在使用过程中能直接调用的API,在源码中的lib目录下可以看到,具体包括http、net、stream、fs、buffer、events等模块。
2.Node bindings
这一层是JavaScript与底层C/C++能够沟通的关键,前者通过bindings调用后者,相互交换数据。
3.Node基础构件
这一层包括支撑Node.js运行的基础构件,是使用C/C++语言编写的,具体包括以下主要模块:
- V8:Google推出的JavaScript VM(JavaScript虚拟机),也是Node.js为什么用JavaScript的关键,它为JavaScript提供了在非浏览器端运行的环境,它的高效是Node.js之所以高效的原因之一。
- libuv:为Node.js提供了跨平台、线程池、事件池、异步I/O等能力,是Node.js如此强大的关键。
- C-ares:提供了异步处理DNS相关的能力。
- http_parser、OpenSSL、zlib等:提供了包括HTTP解析、OpenSSL、数据压缩等功能。
1.1.4 Node.js特点
Node.js的强大体现在很多方面,如事件驱动、异步处理、非阻塞I/O等。这里将介绍Node.js具备不同于其他框架的特点,包括事件驱动、异步编程等内容。
1.事件驱劢
在某些传统的网络编程语言中,都会使用到回调函数。比如:当Socket资源达到某种状态时,注册的回调函数就会执行。在Node.js的设计思想中,是以事件驱动为核心的,它提供的绝大多数API都是基于事件的、异步的风格。以Net模块为例,其中的net.Socket对象就有以下事件:connect、data、end、timeout、drain、error、close等。使用Node.js的开发人员需要根据自己的业务逻辑注册相应的回调函数。这些回调函数都是异步执行的。这意味着虽然在代码结构中这些函数看起来是依次注册的,但是它们并不依赖于自身出现的顺序,而是等待相应的事件触发。
事件驱动的优势在于充分利用了系统资源,执行代码无须阻塞等待某种操作完成,有限的资源可以用于其他的任务。此类设计非常适合于后端的网络服务编程,Node.js的目标也在于此。在服务器开发中,并发的请求处理是一个大问题,阻塞式的函数会导致资源浪费和时间延迟。通过事件注册、异步函数,开发人员可以提高资源的利用率,性能也会改善。
2.异步、非阻塞I/O
从Node.js提供的支持模块中,我们可以看到包括文件操作在内的许多函数都是异步执行的。这和传统语言存在区别。为了方便服务器开发,Node.js的网络模块特别多,包括HTTP、DNS、NET、UDP、HTTPS、TLS等。开发人员可以在此基础上,快速构建Web服务器应用。一个异步I/O的大致流程如图1.2所示。
图1.2 异步I/O的流程
如图1.2中所示,异步I/O流程主要包括以下过程:
(1)发起I/O调用
①用户通过JavaScript代码调用Node核心模块,将参数和回调函数传入核心模块。
② Node核心模块会将传入的参数和回调函数封装成一个请求对象。
③将这个请求对象推入I/O线程池等待执行。
④ JavaScript发起的异步调用结束,JavaScript线程继续执行后续操作。
(2)执行回调
① I/O操作完成后会将结果存储到请求对象的result属性上,并发出操作完成的通知。
②每次事件循环时会检查是否有完成的I/O操作,如果有就将请求对象加入I/O观察者队列中,之后当作事件处理。
③处理I/O观察者事件时会取出之前封装在请求对象中的回调函数,执行这个回调函数,并将result当作参数,以完成JavaScript回调的目的。
Node.js的网络编程非常方便,提供的模块(在这里是HTTP)开放了容易上手的API接口,短短几行代码就可以构建服务器。
3.性能出众
创始人Ryan Dahl在设计的时候就考虑了性能方面的问题,选择C++和V8,而不是Ruby或者其他的虚拟机。Node.js在设计上以单进程、单线程模式运行。事件驱动机制是Node.js通过内部单线程高效率地维护事件循环队列来实现的,没有多线程的资源占用和上下文切换。这意味着面对大规模的HTTP请求,Node.js是凭借事件驱动来完成的。从大量的测试结果分析来看,Node.js的处理性能非常出色,在QPS(Query Per Second,每秒查询率)达到16700次时,内存仅占用30MB(测试环境:RHEL 5.2、CPU 2.2GHz、内存4GB)。
4.单线程
Node.js和大名鼎鼎的Nginx一样,都是以单线程为基础的。这正是Node.js保持轻量级和高性能的关键,也是Ryan Dahl设计Node.js的初衷。这里的单线程是指主线程为“单线程”,所有阻塞的部分交给一个线程池处理,然后这个主线程通过一个队列跟线程池协作。我们编写的js代码部分不用再关心线程问题,代码也主要由一堆callback回调构成,然后主线程在循环过程中适时调用这些代码。
单线程除了保证Node.js高性能之外,还保证了绝对的线程安全,使开发者不用担心同一变量同时被多个线程读写而造成的程序崩溃。
1.1.5 Node.js应用场景
Node.js可以应用到很多方面,可以这么说,从Node.js开始程序员们就能用JavaScript来开发服务器端的程序了。Node.js为前端开发程序员们提供了便利,并在各大网站中承担重要角色,成为开发高并发大型网络应用的关键技术。Web站点早已不仅限于内容的呈现,很多交互性和协作型环境也逐渐被搬到了网站上,而且这种需求还在不断地增长。这就是所谓的数据密集型实时(Data-Intensive Real-Time)应用程序,比如在线协作的白板、多人在线游戏等,这种Web应用程序需要一个能够实时响应大量并发用户请求的平台支撑它们,这正是Node.js擅长的领域。此外,Node.js的跨平台特性也是使用Node.js语言开发流行的另一大原因。
Node.js的主要应用场景如下:
- JSON APIs:构建一个Rest/JSON API服务,Node.js可以充分发挥其非阻塞IO模型以及JavaScript对JSON的功能支持(如JSON.stringfy函数)。
- 单页面、多Ajax请求应用:如Gmail,前端有大量的异步请求,需要服务后端有极高的响应速度。
- 基于Node.js开发UNIX命令行工具:Node.js可以大量生产子进程,并以流的方式输出,这使得它非常适合做UNIX命令行工具。
- 流式数据:传统的Web应用,通常会将HTTP请求和响应看成原子事件,而Node.js会充分利用流式数据这个特点,构建非常酷的应用,如实时文件上传系统Transloadit。
- 准实时应用系统:如聊天系统、微博系统,但JavaScript是有垃圾回收机制的,这就意味着系统的响应时间是不平滑的(GC垃圾回收会导致系统这一时刻停止工作)。如果想要构建硬实时应用系统,Erlang是一个不错的选择。
例如,实时互动交互比较多的社交网站,像Twitter这样的公司,它必须接收tweets并将其写入数据库。实际上,每秒几乎有数千条tweet达到,数据库不可能及时处理高峰时段所需的写入数量。Node成为这个问题解决方案的重要一环。Node能处理数万条入站tweet。它能快速而又轻松地将它们写入一个内存排队机制(例如memcached),另一个单独进程可以从那里将它们写入数据库。Node能处理每个连接而不会阻塞通道,从而能够捕获尽可能多的tweets。
虽然看起来Node.js可以做很多事情,并且拥有很高的性能,但是Node.js并不是万能的,有一些类型的应用,Node.js可能处理起来会比较吃力。例如,CPU密集型的应用、模板渲染、压缩/解压缩、加/解密等操作都是Node.js的软肋。
1.1.6 Node.js在国内的发展
Node.js在初期发展的时候,国内就有大量的开发者开始持续关注了。随着Node.js的不断成熟,很多国内的公司都开始采用这一新技术。Node.js开发者在国内的数量不断增加,并涌现出很多组织和机构来自发地进行推广和技术分享。
国内的各大视频培训网站上都有Node.js开发的培训教程,各大门户网站也都或多或少地采用了Node.js的开发技术,淘宝、网易、百度等有很多项目运行在Node.js之上。阿里云是在这方面比较靠前的公司,它们的云平台率先支持Node.js的开发。淘宝也为Node.js搭建了国内的NPM镜像网站,方便国内的开发者下载各种开发包。
以下是关于Node.js中文资源的汇总清单:
(1)Node.js官方网站,是Node.js在国内的官方网站,有关于Node.js最新版本的下载和新闻,丰富的文档资料非常值得认真学习,是Node.js开发爱好者不容错过的网站,网址为https://nodejs.org/zh-cn/。
(2)CNode社区,由一批热爱Node.js技术的工程师发起,已经吸引了互联网各个公司的专业技术人员加入,是目前国内非常具有影响力的Node.js开源技术社区,致力于Node.js的技术研究,并有论坛会定期组织一些技术交流活动,网址为https://cnodejs.org。
(3)Node.js中文网,是一个专业的Node.js中文知识分享社区,致力于普及Node.js知识,分享Node.js研究成果,努力推进Node.js在中国的应用和发展,网站有大量的技术博客和文章,各个级别的开发者都能找到适合自己学习的资料,网址为www.nodejs.cn。
(4)淘宝NPM镜像,是一个完整npmjs.org镜像,可以用此代替官方版本,同步频率为每10分钟一次,以保证尽量与官方服务同步,网址为https://npm.taobao.org/。
(5)Node.js & HTML5论坛,也是一个学习Node.js和前端开发技术非常好的网站,每天都有大量原创文章发布,并且技术问题可以很快被回答。当然,如果愿意为其他人解答技术问题或者进行技术分享也是非常受欢迎的。其网址为http://forjs.org/。
每年的JavaScript中国开发者大会和各种Node.js分享沙龙也都是很好的学习Node.js开发技术和交流的机会。一个开发者要时刻保持谦虚的心态,并不断学习新的技术。这对开发者来说是一种基本能力和素养。