Part 0 :Node.js简介
a)Node.js简介
b)什么是Node.js?
c)基本原理
a)Node.js简介
Node.js 诞生于 2009 年,由 Joyent 的员工 Ryan Dahl 开发而成,之后 Joyent 公司一直扮演着 Node.js 孵化者的角色。由于诸多原因,Ryan 在2012年离开社区,随后在2015年由于 Node 贡献者对 es6 新特性集成问题的分歧,导致分裂出iojs,并由 iojs 发布1.0、2.0和3.0版本。由于 iojs 的分裂最终促成了2015年Node基金会的成立,并顺利发布了4.0版本。Node.js基金会的创始成员包括 Google、Joyent、IBM、Paypal、微软、Fidelity 和 Linux基金会,创始成员将共同掌管过去由 Joyent 一家企业掌控的 Node.js 开源项目。此后,Node.js基金会发展非常好,稳定地发布5、6、7、8等版本,截止发稿最新版本已经是8.6,长期支持版本是6.11。
Node.js 不是一门语言也不是框架,它只是基于 Google V8 引擎的 JavaScript 运行时环境,同时结合 Libuv 扩展了 JavaScript 功能,使之支持 io、fs 等只有语言才有的特性,使得 JavaScript 能够同时具有 DOM 操作(浏览器)和 I/O、文件读写、操作数据库(服务器端)等能力,是目前最简单的全栈式语言。
早在2007年,Jeff Atwood 就提出了著名的 Atwood定律
任何能够用 JavaScript 实现的应用系统,最终都必将用 JavaScript 实现
目前 Node.js 在大部分领域都占有一席之地,尤其是 I/O 密集型的,比如 Web 开发,微服务,前端构建等。不少大型网站都是使用 Node.js 作为后台开发语言的,用的最多的就是使用Node.js做前端渲染和架构优化,比如 淘宝 双十一、去哪儿网 的 PC 端核心业务等。另外,有不少知名的前端库也是使用 Node.js 开发的,比如,Webpack 是一个强大的打包器,React/Vue 是成熟的前端组件化框架。
Node.js通常被用来开发低延迟的网络应用,也就是那些需要在服务器端环境和前端实时收集和交换数据的应用(API、即时聊天、微服务)。阿里巴巴、腾讯、Qunar、百度、PayPal、道琼斯、沃尔玛和 LinkedIn 都采用了 Node.js 框架搭建应用。
另外, Node.js 编写的包管理器 npm 已成为开源包管理领域最好的生态,截止到2017年10月份,有超过47万模块,每周下载量超过32亿次,每个月有超过700万开发者使用npm。
当然了,Node.js 也有一些缺点。Node.js 经常被人们吐槽的一点就是:回调太多难于控制(俗称回调地狱)和 CPU 密集任务处理地不是很好。但是,目前异步流程技术已经取得了非常不错的进步,从Callback、Promise 到 Async函数,可以轻松地满足所有开发需求。至于 CPU 密集任务处理并非不可解,方案有很多,比如通过系统底层语言 Rust 来扩展 Node.js,但这样会比较麻烦。笔者坚信在合适的场景使用合适的东西,尤其是在微服务架构下,一切都是服务,可以做到语言无关。如果大家想使用 JavaScript 做 CPU 密集任务,推荐 Node.js 的兄弟项目 fibjs,基于纤程(fiber,可以简单理解为更轻量级的线程),效率非常高,兼容npm,同时没有异步回调烦恼。
b)什么是Node.js?
按照 Node.js官方网站主页 的说法:
Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient. Node.js' package ecosystem, npm, is the largest ecosystem of open source libraries in the world.
从这段介绍来看,解读要点如下
- Node.js 不是 JavaScript 应用,不是语言(JavaScript 是语言),不是像 Rails(Ruby)、 Laravel(PHP) 或 Django(Python) 一样的框架,也不是像 Nginx 一样的 Web 服务器。Node.js 是 JavaScript 运行时环境
- 构建在 Chrome’s V8 这个著名的 JavaScript 引擎之上,Chrome V8 引擎以 C/C++ 为主,相当于使用JavaScript 写法,转成 C/C++ 调用,大大的降低了学习成本
- 事件驱动(event-driven),非阻塞 I/O 模型(non-blocking I/O model),简单点讲就是每个函数都是异步的,最后由 Libuv 这个 C/C++ 编写的事件循环处理库来处理这些 I/O 操作,隐藏了非阻塞 I/O 的具体细节,简化并发编程模型,让你可以轻松的编写高性能的Web应用,所以它是轻量(lightweight)且高效(efficient)的
- 使用
npm
作为包管理器,目前npm
是开源库里包管理最大的生态,功能强大,截止到2017年12月,模块数量超过 60 万+
大多数人都认为 Node.js 只能写网站后台或者前端工具,这其实是不全面的,Node.js的目标是让并发编程更简单,主要应用在以网络编程为主的 I/O 密集型应用。它是开源的,跨平台,并且高效(尤其是I/O处理),包括IBM、Microsoft、Yahoo、SAP、PayPal、沃尔玛及GoDaddy都是 Node.js 的用户。
c)基本原理
下面是一张 Node.js 早期的架构图,来自 Node.js 之父 Ryan Dahl 的演讲稿,在今天依然不过时,它简要的介绍了 Node.js 是基于 Chrome V8引擎构建的,由事件循环(Event Loop)分发 I/O 任务,最终工作线程(Work Thread)将任务丢到线程池(Thread Pool)里去执行,而事件循环只要等待执行结果就可以了。
核心概念
- Chrome V8 是 Google 发布的开源 JavaScript 引擎,采用 C/C++ 编写,在 Google 的
Chrome
浏览器中被使用。Chrome V8 引擎可以独立运行,也可以用来嵌入到 C/C++ 应用程序中执行。 - Event Loop 事件循环(由
libuv
提供) - Thread Pool 线程池(由
libuv
提供)
梳理一下
- Chrome V8 是 JavaScript 引擎
- Node.js 内置 Chrome V8 引擎,所以它使用的 JavaScript 语法
- JavaScript 语言的一大特点就是单线程,也就是说,同一个时间只能做一件事
- 单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。
- 如果排队是因为计算量大,CPU 忙不过来,倒也算了,但是很多时候 CPU 是闲着的,因为 I/O 很慢,不得不等着结果出来,再往下执行
- CPU 完全可以不管 I/O 设备,挂起处于等待中的任务,先运行排在后面的任务
- 将等待中的 I/O 任务放到 Event Loop 里
- 由 Event Loop 将 I/O 任务放到线程池里
- 只要有资源,就尽力执行
我们再换一个维度看一下
核心
- Chrome V8 解释并执行 JavaScript 代码(这就是为什么浏览器能执行 JavaScript 原因)
libuv
由事件循环和线程池组成,负责所有 I/O 任务的分发与执行
在解决并发问题上,异步是最好的解决方案,可以拿排队和叫号机来理解
- 排队:在排队的时候,你除了等之外什么都干不了
- 叫号机:你要做的是先取号码,等轮到你的时候,系统会通知你,这中间,你可以做任何你想做的事儿
Node.js 其实就是帮我们构建类似的机制。我们在写代码的时候,实际上就是取号的过程,由 Event Loop 来接受处理,而真正执行操作的是具体的线程池里的 I/O 任务。之所以说 Node.js 是单线程,就是因为在接受任务的时候是单线程的,它无需进程/线程切换上下文的成本,非常高效,但它在执行具体任务的时候是多线程的。
Node.js 公开宣称的目标是 “旨在提供一种简单的构建可伸缩网络程序的方法”,毫无疑问,它确实做到了。这种做法将并发编程模型简化了,Event Loop和具体线程池等细节被 Node.js 封装了,继而将异步调用 Api 写法暴露给开发者。真是福祸相依,一方面简化了并发编程,另一方面在写法上埋下了祸根,这种做法的好处是能让更多人轻而易举地写出高性能的程序!
在Node.js Bindings层做的事儿就是将 Chrome V8 等暴露的 C/C++
接口转成JavaScript Api,并且结合这些 Api 编写了 Node.js 标准库,所有这些 Api 统称为 Node.js SDK,后面模块章节会有更详细的讨论。
微软在2016年宣布在MIT许可协议下开放 Chakra 引擎,并以 ChakraCore
为名在 Github 上开放了源代码,ChakraCore
是一个完整的 JavaScript 虚拟机,它拥有着和 Chakra
几乎相同的功能与特性。微软向 Node.js 主分支提交代码合并请求,让 Node.js 用上 ChakraCore
引擎,即 nodejs/node-chakracore 项目。实际上微软是通过创建名为 V8 shim
的库的赋予了 ChakraCore
处理谷歌 Chrome V8 引擎指令的能力,其原理示意图如下
目前,Node.js 同时支持这2种 JavaScript 引擎,二者性能和特性上各有千秋,ChakraCore
在特性上感觉更潮一些,曾经是第一个支持 Async函数
的引擎,但目前 Node.js 还是以 Chrome V8 引擎为主, ChakraCore
版本需要单独安装,大家了解一下就好。