NodeJs 简明教程将从零开始学习 NodeJs 相关知识,助力 JS 开发者构建全栈开发技术栈!
本文是 NodeJs 简明教程的第一篇,将介绍 NodeJs 整体架构以及重点概念。
NodeJs 究竟是什么
来看一段官方的说法[1]:
As an asynchronous event driven JavaScript runtime, Node is designed to build scalable network applications. In the following “hello world” example, many connections can be handled concurrently. Upon each connection the callback is fired, but if there is no work to be done, Node will sleep.
Google 翻译版本:
作为异步事件驱动的 JavaScript 运行时,Node 旨在构建可伸缩的网络应用程序。在下面的“hello world”示例中,可以同时处理许多连接。在每次连接时都会触发回调,但是如果没有工作要做,Node 将会休眠。
结合上面的介绍,我们可以得出一个结论:
NodeJs 的本质是一个 Javascript 运行时。该运行时基于异步事件驱动进行运作。
异步
本文中的异步指异步 IO。维基百科对异步 IO 的定义[2]:
异步 IO 是计算机操作系统对输入输出的一种处理方式:发起 IO 请求的线程不等 IO 操作完成,就继续执行随后的代码,IO 结果用其他方式通知发起 IO 请求的程序。与异步 IO 相对的是更为常见的“同步(阻塞)IO”:发起 IO 请求的线程不从正在调用的 IO 操作函数返回(即被阻塞),直至 IO 操作完成。
一言以蔽之就是:
执行 IO 请求后,调用方不等执行结果就继续执行下面的代码,IO 操作完成后执行者会告诉调用者“我执行完了”。在 NodeJs 中通知方式是“回调”。
事件驱动
事件驱动是相对 线程驱动 而言的。 线程驱动 下服务器为每个请求新建一个线程去处理。
维基百科对事件驱动的定义[3]:
事件驱动程序模型下的系统,基本上的架构是预先设计一个事件循环所形成的程序,这个事件循环程序不断地检查当前要处理的信息,根据要处理的信息运行一个触发函数进行必要的处理。其中这个外部信息可能来自一个目录夹中的文件,可能来自键盘或鼠标的动作,或者是一个时间事件。
以 NodeJs 的 HTTP 服务器为例,当调用 server.listen
函数时,NodeJs 就会创建一个事件循环,当有客户端请求过来时,NodeJs 将该请求入队列进行后续处理,主线程以及轮询客户端请求并入队列,队列中的请求执行完毕后会通过回调函数的形式通知主线程,如此循环。
Javascript 运行时
Javascript 运行时是个比较复杂的概念,本文在介绍 Javascript 运行时 之前介绍一下 Javascript 引擎。
Javascript 引擎
维基百科的定义[4]:
JavaScript 引擎是一个专门处理 JavaScript 脚本的虚拟机,一般会附带在网页浏览器之中。
个人理解:
Javascript 引擎主要是对 Javascript 代码进行词法、语法等分析,通过编译器将代码编译成可执行的机器码让计算机去执行。
目前业内出名的 Javascript 引擎非 V8 莫属了。
运行时的组成
Javascript 可以运行在浏览器,也可以运行在服务器 (NodeJs) 中,有些 API 或者对象只有浏览器有(比如 DOM,BOM 等),而有些 API 或者对象只有服务器中有(如文件操作,HTTP 服务器等)。
Javascript 运行时包括了 Javascript 引擎、特定环境 API、事件循环和事件队列。
NodeJs 架构图
NodeJs 由 C ++ 语言基于 libuv 开发,分层设计,Javascript 只是其基于 V8 提供的上层接口,换句话说,如果把上层接口换成其他语言实现,比如换成 PHP 实现,那么 PHP 就可以实现异步事件驱动的服务器,运行时名称就成为 NODE-PHP。
- Node standard library NodeJs 标准库,也是直接提供给开发者调用的顶层代码
- Node bindings Javascript 和 libuv 在该层进行通信,基于 V8 打通语言壁垒
- V8 执行 JS 代码
- libuv 高性能异步 I /O、事件驱动、线程池的库,也是 NodeJs 高性能的保证
- C-ares 提供异步 DNS
- http_parser、OpenSSL、Zlib 提供 HTTP 解析、openssl 加解密、数据压缩等接口
NodeJs 到底是不是单线程
不是,主线程 Javascript 线程是单线程,libuv 提供线程池,NodeJs 不仅仅是一个 Javascript 引擎,而是一套运行时,不能将 Javascript 线程孤立出来。
NodeJs 为什么这么快
- 单线程解决了多线程环境下线程切换开销以及可能的线程同步开销
- 异步 + 事件驱动保证了 NodeJs 主线程不会阻塞,会一直接受请求(这也是受人诟病的地方,其他语言实现的服务器,请求过大会排队处理,NodeJs 会将请求全部入队,导致内存暴涨)
NodeJs 优缺点以及适合的场景
- 由于主线程 Javascript 线程是单线程,所以主线程不能做 CPU 密集操作(比如什么加解密之类的,这种操作只能有 Javascript 线程运行,会阻塞事件循环),所以 NodeJs 适合 I / O 密集场景,比如常见的(TCP/HTTP 服务器)
- 对于前端开发者来说,几乎没有语言门槛
- 跨平台,NodeJs 在主流操作系统都有对应的二进制程序
- 标准库强大,第三方库也很多,降低了造轮子成本
- 易于部署,服务器安装一个 NodeJs 程序配合 NPM 包管理器即可运行,不用像 PHP 那样还要安装扩展,配置前端 HTTP 服务器
结语
欢迎继续关注本系列文章。
参考文献
- About NodeJs
- 异步 IO
- 事件驱动
- Javascript 引擎