NodeJs 介绍
1. 定义
我们学习掌握一门新技术时候,第一步应当从它的官方文档开始认知,打开Node.js官方文档可以看到比较显眼的一句话是。
Node.js 是建立在 Chrome V8 上面的一个 JavaScript 运行环境
通俗点解释: js 属于一种脚本性语言,然而脚本语言运行需要一个解析器来解析,对于我们原来写的 js 代码大部分都是运行在网页上,所以浏览器本身就担当了解析器的角色。而现在对于独立运行在服务器的 js 代码,node 就属于那个解析器。
V8 属于 Google Chrome 浏览器的一个高性能引擎,可以直接将 JavaScript 编译为本地机器代码,而其他的语言如 PHP 和 Ruby,Java 每次访问时都必须通过解释器运行。
2. 发展史
node 的诞生到目前也走过了 10 多年,一路走来辉煌过、质疑过、突围过、到目前,可谓天生我材必有用,千金散尽还复来。
所以笔者自己做了一个时间轴图来和大家一起见证下 node 在历史洪流中比较重要的时刻。
此外我还收集整理了两篇关于 NodeJS 创始人 Ryan Dahl 的访谈录,一篇是 10 年前的 Ryan Dahl 的访谈和一篇近期对 Ryan Dahl 的访谈,虽然内容不是很多关于 NodeJs 技术层面的问题和教程,但记者对 Ryan Dahl 提的一些有趣的问题,和 Ryan Dahl 的回答会让你感觉很有趣也会让你对 Node 有一些不一样的见解,如果大家对访谈录感兴趣的可以关注我的微信公众号一起学 Node 中查看,可以带你了解 NodeJs 的前世今生。
3. 特性
我们使用 node 可能听到最多的几个特性就是
单线程
非阻塞 I/O
Event Loop
3.1 单线程
Node JS 的应用程序,是使用单线程以及事件循环体系结构组合,来处理客户端请求的,但实际上我们要明白不要误解的一点它只是主事件循环是单线程的,其他的比如 I / O 处理相关的操作是有单独的线程去处理,因为 Node JS 中很多的 I / O API 在设计上本就是异步/非阻塞的,为了就是更方便适应主事件循环。
为此笔者也做了两个小图给大家描述下 Node 与传统服务器端环境之间的区别,可以看到不一样的点。
图 1:
图二:
图 1 中
服务器分配了多个线程,如果程序遇到正在等待 I / O,则会阻塞整个线程只有等待
每个客户端的请求中都有自己的线程进行资源分配
服务不可能开启无限线程,所以当用户多了的时候用户只有排队等待服务器有可用空闲线程,新的用户才可以请求
图 2 中
看着就简约很多(哈哈)
node 服务接收了所有请求,然后交给主线程直接运行,一路绿灯只有等到全部等待的时候才会停。
当然关于单线程多线程的问题知识点是有很多内容扩展的,大家可以关注后续章节为大家分享。
3.2 非阻塞 I/O
说到非阻塞我们可能想到的就是和阻塞概念放在一起讨论
阻塞
阻塞的定义是指在 Node.js 程序中,其它 JavaScript 语句的执行,必须等待一个非 JavaScript 操作完成。这是因为当阻塞发生时,事件循环无法继续运行 JavaScript。阻塞执行的方法都为同步执行执行,例如我们常见的文件读取案例就是典型的同步读取操作。
非阻塞
前面提到当阻塞发生时,事件循环无法继续运行 JavaScript。在非阻塞时当任何请求发送到服务器时,它不会将发送过来请求排队缓存。接受请求后并开始执行(如果它阻塞了操作),然后将其发送到对应的工作线程区域进行处理,并在代码执行完成后立即为该线程注册一个回调,然后触发相同的回调并进入事件队列,之后再次由事件循环处理响应发送到相应的客户端,在这里它几乎不会阻塞。
有朋友刚开始可能会认为异步和非阻塞差不多(我当时也是这么认为的),这种想法是错误的。
异步和非阻塞虽然都达到了我们并行 I/O 的目的。但是从计算机内核 I/O 而言,异步 / 同步和阻塞 / 非阻塞实际上是两回事
3.3 Event Loop
前两个是比较好容易理解, Event Loop 这个特性很少有人说可以完完全全搞明白它到底是怎么运转的,更多的是大家通过一些优秀文章和讲解以及一些经验来理解,Event Loop 也是贯穿这 node 的核心,熟悉理解对于 node 学生有大有帮助,在描述之前先和大家分享一点很多人对此的一些误解点。
事件循环和用户代码运行在不同的线程中
误解点:有一个主线程在运行用户的 js 代码,另一个线程运行事件循环。每次异步操作发生时,主线程将工作移交给事件循环线程,一旦完成,事件循环线程将等待主线程以执行回调
实现情况:只有一个线程执行 js 代码,就是事件循环运行的线程,回调的执行是由事件循环完成的
所有的异步操作都由线程池处理
误解点:异步操作(如处理文件系统、执行出站 HTTP 请求或与数据库通信)都是加载到 libuv 提供的线程池中进行执行处理
实际情况:Libuv 在默认情况下创建一个线程池,其中有四个线程可以将异步工作转移到其中,然而如今的操作系统已经为许多 I/O 任务都提供了异步接口,在一定情况下,libuv 将使用这些异步接口进行调用,避免使用线程池,只有在没有其他方法的情况下,线程池才会用于异步 I/O
libuv 是一个基于事件驱动的跨平台抽象层,封装了不同操作系统一些底层特性,对外提供统一的 API
事件循环类似于堆栈或队列
误解点:事件循环连续地遍历异步任务进行先进先出,并在任务完成时执行回调
现实情况:虽然涉及到类似队列的结构,但事件循环不会遍历并处理堆栈,作为事件循环是一组以循环方式处理特定任务的阶段
这几个是很多同学比较容易误区的几个点,看到官方的一些描述是:
事件循环是 Node.js 处理非阻塞 I/O 操作的机制——尽管 JavaScript 是单线程处理的——当有可能的时候,它们会把操作转移到系统内核中去。
看了这些话可能不会所动,说的还是太官方了语言描述太简单了,就像我们的新闻描述一样字越少说明事越不简单,先不要急,再来看看对于事件循环机制的整个周期阶段说明,官方给出了一张图
从上图中大致可看出 事件循环的流程和大概:
数据输入–>轮询阶段(poll)–>检查阶段(check)–>关闭事件回调阶段(close callback)–>定时器检测阶段(timer)–>I/O 待定回调阶段(pending callbacks)–>闲置阶段(idle, prepare)–>轮询阶段(反复循环)
timers :本阶段执行已经被 setTimeout() 和 setInterval() 调度的回调函数
ending callbacks:执行延迟到下一个循环迭代的 I/O 回调
idle, prepare :仅系统内部使用
poll :检索新的 I/O 事件,执行和 I/O 相关的回调内容(几乎所有情况下,除了关闭的回调函数,- 那些由计时器和 setImmediate() 调度的之外)
check :setImmediate() 回调函数在这里执行
close callbacks:一些将要关闭的回调函数,例如:socket.on('close', ...)
关于此知识点更详细的内容可查看官方文档
大家想深入了解的可以关注后续文章,在核心篇里会讲解扩展更多 node 相关知识点和实践
在看到两个比较好的动画图解释浏览器和 node 的事件循环的流程对比
浏览器的:
(来源:Fundebug)
node 的:
(来源:Fundebug)
4. Node 应用与劣势
知道了它的诞生、特性、原理,下面我们来看看它的一些应用和不足,毕竟技术只有落地才是真理,不然说的再多都是白搭
4.1 应用领域
它的正统应该非 api server 服务莫属了
各种命令行工具,例如 比较实用的 vue-cli
跨平台客户端 像微信小程序使用的 nw.js Electron 等
前端组件化构建相关工具 Gulp 、Webpack 等
编辑器 Atom 、 VSCode
还有很多就不例举了,这些领域不是说是 node 专属的只是相对于其他从各方面考虑要适合一些
4.2 应用者
阿里:光一个 egg.js 就是最好的说明了
腾讯:微信小程序,云服务部分功能
网易:pomelo 框架
Uber:每天使用 Node 处理 20 亿个远程过程调用(Remote Procedure Call,RPC)
PayPal:根据 Node.js at PayPal,使用 Node.js 之后,应用开发速度提高了 2 倍;代码量减少了 33%;文件数目减少了 40%,并且,每秒处理的请求数增加了 2 倍,接口的请求时间减少了 35%
像微软谷歌这些公司多年前就加入基金会了
因为这些头部公司的接入使用说明 node 的发展还是很成功的,但技术只有通过多个方面综合考虑如人力成本 学习成本等才能定下来,node 发展迅速与采用 js 语言为基础学习成本低使用人数多,并和强大的 npm 社区密不可分,但这些不能掩盖它的一些缺点导致 npm 包的质量参差不齐。
只有正面 node 的一些缺点才可以更理性的看待它,就像 TJ 在决定离开 node 的时候说的,如果是 web 开发他还是会采用 node,只不过他要开始转向分布式系统的开发了,所以不适合了。
4.3 劣势
计算密集型应用 如果非要把 Javascript 和 C 去拼计算性能,结果可想而知。
内存控制 把 js 和 java 比较复杂数据类型定义的话,也是不明智的。因为 js 的面向对象是基于 JSON 的,而 java 是直接使用内存结构。所以,通过 JSON 序列化和反序列的过程控制内存,js 在开始就已经败了。
静态服务器,I/O 密集应用固然是 node 的优势,但是你非要把它和 Nginx 这种专业处理静态资源服务放一起对比的话那本身就已经输了
本身不需要异步处理的应用:比如自行化脚本,如果使用 node 的话效果上先不说好不好,光从写代码人文角度,一些异步回调的处理就给我们带来不必要的麻烦
5. 环境安装
说了这么多从现在开始让我们进行编码领域吧,毕竟实实在在的代码看着还是比较踏实
5.1 工具包搭建
直接在官方网站下载 Node.js 源码或预编译安装包即可
控制台输入
出现此标示说明安装成功,12.16.3 为当前最新 LTS 版本
工具三剑客安装 node 后我们可以在安装几个软件,可以让我们使用 node 更加的便捷 npm nvm nrm 这三个,主要是 nvm 和 nrm 因为 npm 一般安装 node 的时候已经自带了。
5.1.1 npm
目前的版本里面都是自己携带了不用单独下载
5.1.2 nvm
nvm 功能是切换 node 各个版本
不过 nvm 的官方版本只支持 Linux 和 Macnvm官方文档。对 Windows 用户不是很友好,针对 windows 可以使用nvm-windows其他设置官方里面写了很清楚。
nvm 中常用的几种方式
nvm install ** 安装指定版本,如安装 v12.0.0,既可 nvm install v12.0.0
nvm uninstall ** 删除已安装的指定版本,如删除 nvm uninstall v12.0.0
nvm use ** 切换使用指定的版本
nvm ls ** 列出所有安装的版本
5.1.3 nrm
nrm 作用是切换 npm 源的,默认的源是 npm 官方源https://registry.npmjs.org/
安装直接
执行 nrm ls 就可以看到你所指定的所有源
如果想添加一个源的话直接执行
如果采用官方的源下载的时候可能会很慢,推荐大家安装淘宝的国内源https://registry.npm.taobao.org
更多细节可在nrm官方文档
最后在控制台输入 node 命令进行操作,就可以开始 node 开发之旅了
我是来自《小风以北》公众号的小风,我们下期再见
版权声明: 本文为 InfoQ 作者【小风以北】的原创文章。
原文链接:【http://xie.infoq.cn/article/f553cefd91a31b1050b57485d】。文章转载请联系作者。
评论