Node简介

1. node是什么?

  node是一个基于V8引擎的js运行环境,其特点为:

  (1)异步I/O:用户线程在操作I/O时根本不考虑IO的执行,全部交给IO线程池去处理,用户线程只用等待一个完成信号即可—从语言层面上可以很自然进行并行I/O操作,每个调用之间都无需等待之前的IO调用结束。

  eg:以读文件为例:

 *  同步IO方式:耗时M+N;

     * fs.readFileSync('1.text'); //耗时M

     * fs.readFileSync('1.text'); //耗时N

* 异步IO方式:耗时max(M, N);

      * fs.readFile('1.text', function(){ ... }); //耗时M

      * fs.readFile('2.text', function(){ ... }); //耗时N

异步IO调用示意图

  (2)事件与回调函数:

       * 将事件从前端浏览器引入后端,配合异步IO,将事件点暴露给业务逻辑;

       * 回调函数作为最好的接收异步调用返回数据的方式;


Node服务器绑定了一个request事件,对于请求对象,为其绑定data事件和end事件

  (3)单线程:Node自身是多线程的,而js线程与其余线程是无法共享任何状态的,而且js代码无法并行执行(同一时间段只能执行一段代码);

 * 优点:

      * 不存在多线程中死锁、状态同步等问题;

      * 不存在创建线程和执行期上下文切换的开销;

* 缺点:

      * 无法利用多核CPU;

      * 错误会引起整个应用退出,健壮性不高;

      * 大量计算占用CPU导致cpu时间片不能释放 — 后续的异步IO发不出调用,已完成的异步IO回调函数得不到执行;

* 解决长时间计算:

      * 编写c/c++扩展(后续文章会介绍)的方式更高效利用CPU;

      * 通过子进程的方式,讲一部分node进程当作常驻服务进程用于计算,然后利用进程间的消息来传递结果,将计算和IO分离,充分利用多CPU;

(4)跨平台:基于libuv实现;

libuv已成为许多系统实现跨平台的基础组件

2. node的构成

nodejs架构

(1)V8:以C++实现的高性能的js引擎;

    * 将编写的js代码编译成机器码,然后再执行;

    * 提供c++函数接口,为nodejs提供v8初始化,创建context、scope等;

(2)libuv:为builtin modules提供了API,这些API用来支持请求和数据返回的异步处理方式(回调函数在libuv触发);

libuv的架构
libuv细节流程图

(3)builtin modules:由c++写的各类模块,包含file stream、crypto、zlib等基础功能;

(4)native modules:js写成供应用程序调用的库,依赖于builtin modules来获取相应的服务支持;

    综上:暴露给开发者的接口是native modules,当我们发起请求,请求自上而下穿越native modules,通过builtin modules将请求传送至v8、libuv和其他辅助服务,请求结束则从下回到上层,最终调用我们的回调函数。

3. 模块分类

(1)文本模块

    * js文本模块:自己编写并保存的.js文件;

    * JSON模块:JSON文件;

    * c/c++扩展:使用node-gyp扩展构建工具将扩展文件编译为.node文件(windows下是.dll 文件,*nix下是.so文件,libuv屏蔽了平台的差异,在node中统一为.node文件) 进入node命名空间中,通过process.dlopen()加载代码执行;

(2)核心模块

    * js核心模块:node项目的lib目录下,Node项目编译时先会将lib目录下的js文件编译成c/c++文件—以字符串的形式进入二进制文件(存在于node命名空间中),在调用js核心模块时通过process.binding('Natives')加载并编译执行—获取js核心模块的源代码字符串,再将其转为函数编译并执行,将其结果缓存在 NativeModule._cache对象上;

    注:两个过程的编译不一样:第一个编译过程只是将js源代码转为字符串后注册进c里的数组中,然后生成.h文件进入node环境中;第二次编译是将js源代码编译成可执行的二进制文件;

    * c/c++核心模块:内建模块,node项目的src目录下,也是通过.h头文件将模块添加到node_module_list数组中,将src目录的内建模块名写入node-gyp的‘target_name’的'node'节点的source中,在编译整个Node项目的时候,将其代码编译为可执行文件,在调用时通过process.binding(内建模块名)  协助加载内建模块编译后的代码,直接执行即可;

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容