为什么是 JavaScript
创始者认为 web 服务器有两个要点,事件驱动和非阻塞 I/O。
为什么是 Node
最初创始者想将其命名为 web.js 也就是一个 web 服务器,但是项目的发展超过了他最初单纯开发一个 web 服务器的想法,变成了构建网络应用的基础框架。
Node 给 JavaScript 带来的意义
V8 给 Chrome 浏览器带来了强劲的心脏,也成为了 JavaScript 称为 node 语言的很大权重值。
Chrome 浏览器和 Node 组件构成 区别如下图:
浏览器中,除了 v8 引擎还有一个 webkit 布局引擎,HTML5 使得浏览器开放了很多功能给前端的 HTML 及 JavaScript,但是HTML5 的统一却相对缓慢,JavaScript 作为一个图灵完备的语言,受限于浏览器中间层提供的支持。
除了 HTML 、webkit 和 显卡这些UI 相关的技术没有支持外,Node 结构与 Chrome 十分相近。
它们都是基于事件驱动的一步架构,浏览器通过事件驱动来服务界面上的交互,Node 通过事件驱动来服务 I/O。
Node 中,JavaScript 可以随心所欲的访问本地文件,搭建 websocket 服务端,可以连接数据库,可以使用多进程。
JavaScript 不再限制于浏览器与 CSS 样式表、DOM 树。
Node 不处理 UI,但用浏览器相同的机制和原理运行。
打破了 JavaScript 只能在浏览器中运行的局面,降低了前后端转换所需要的上下文交换代价。
Node 的特点
异步 I/O
前端常用到的就是通过 AJAX 请求数据,发送和收到之间有一个时间的间隔,在收到数据后后续代码立即执行,但是收到的时间确是不确定的,这是一种只注重结果,不注重过程的表现。如下图所示。
在 Node 中异步 I/O 也很常见,可以用读取文件为例:
const fs = require('fs');
fs.readFile('../Desktop/IMG_3648.jpeg', (err, file) => {
console.log(file);
console.log('文件读取完成');
});
console.log('发起读文件的流程!');
代码中示例“发起读取文件的流程”先于“文件读取完成”执行,何时完成基于核实完全读取内容,执行流程如下图:
在 Node 中,我们可以从语言层面很自然的进行并行的 I/O 操作,每个调用之间无需等待之前的 I/O 调用完成,读取两个 文件的最慢速度取决于读取最慢的那个文件的耗时。对于同步 I/O 则是两个文件的综合。
事件与回调函数
事件的编程方式具有轻量级、松耦合、只关注实物点等优势,但是在多个异步任务的场景下,事件与事件之间各自独立,如何协作是一个问题。 函数作为JavaScript 的第一等公民,将函数作为对象传递给方法作为实参进行调用。回调函数是最好的接受异步调用返回数据的方式。
单线程
Node 保持了JavaScript 单线程的特点,单线程最大的好处就是无需像多线程一样处处在意状态同步的问题,没有死锁存在,也没有线程上下文交换带来的性能开销。 单线程的弱点:
- 无法利用多核 CPU
- 错误会引起整个应用退出,应用的健壮性指的考验。
- 大量计算占用 CPU 导致无法继续调用异步 I/O 为解决这些问题,Google 开发了 Gears, HTML5 标准增加了 web workers 标准,Google 放弃了 Gears 支持 web workers。 Web workers 可创建线程进行计算,已解决 JavaScript 大计算阻塞 UI 渲染的问题。 Node 采用了与 web workers 相同的思路解决单线程中大量计算的问题: child_process。
跨平台
最初的 node 只可在 Linux 上使用,后续增加了 windows 平台的支持。
Node 应用场景
I/O 密集型
I/O 密集型的优势主要在于 Node 利用时间循环的处理能力,而不是启动每一个线程为每一个请求服务,资源占用极少,所以 Node 面向网络并擅长并行 I/O。
一定程度上的对于 CPU 密集型业务的支持
CPU 密集型业务在Node 上的支持并不可怕,但是会带来的挑战是:JavaScript 作为单线程的语言,CPU 密集型业务会占用长时间的线程进行计算,导致 CPU 时间片无法释放,使得后续的 I/O 无法发起,但是适当调整和分解大型运算任务为多个小任务,使得运算可以及时释放,不阻塞 I/O 调用的发起,这样既可以享受到异步 I/O 的好处,也能充分利用 CPU。
如果是纯 CPU 的业务,可以通过两种方式进行充分利用 CPU
- Node 可以通过编写 C/C++ 扩展的方式高效利用 CPU。
- 如果单线程的 Node 不能满足要求,甚至通过 C/C++ 扩展之后还不够,可以通过紫禁城的方式,将一部分 Node 进程当做常驻服务进程用于计算,然后通过进程之间通信来传递消息,将 I/O 与计算分离。
与遗留系统和平共处
Node 可以通过将旧有的接口作为数据源,发挥并行优势,不关心背后用什么语言实现。
分布式应用
Node 高效利用 I/O 的过程也是高效使用数据库的过程,对于 Node 而言,使用数据库这种行为只是一次普通的 I/O。对数据库而言却是一次复杂的计算,也是充分压榨硬件资源的过程。
使用者
- 前后端编程语言环境统一,如: 雅虎
- Node 带来的高性能 I/O 用于实时应用,如:voxer 实时语音
- 并行 I/O 使得使用者可以更高效的利用分布式环境。
- 并行 I/O 有效利用稳定接口提升 web 渲染能力。
- 云计算平台提供 Node 支持。
- 游戏开发领域
- 工具类应用