秒速赛车技巧-秒速赛车规律_官网-秒速赛车走势图
当前位置:主页 > 秒速赛车技巧 > 正文

秒速赛车是哪里的开奖Nodejs 原生模块开发方式变

  蚂蚁金服高级工程师,曾负责大搜车无线架构组中间件团队。开源爱好者,Toshihiko 作者、阿里云 ONS SDK 作者,Node.js 核心贡献者之一。

  在 Node.js 开发领域中,原生 C++ 模块的开发一直是一个被人冷落的角落。但是实际上在必要的时候,用 C++ 进行 Node.js 的原生模块开发能有意想不到的好处。

  性能提升。很多情况下,使用 C++ 进行 Node.js 原生模块开发的性能会比纯 Node.js 开发要高,少数情况除外。开发成本节约。在一些即有的 C++ 代码上做封装,开发成本远远低于从零开始写 Node.js 代码。Node.js 无法完成的工作。个别情况,开发者只能得到一个库的静态连接库或者动态链接库以及一堆 C++ 头文件,其余都是黑盒的,这种情况就不得不使用 C++ 进行模块开发了。

  本文将从早期的 Node.js 开始,逐渐披露 Node.js 原生 C++ 模块开发方式的变迁。一直到最后,会比较详细地对 Node.js v8.x 新出的原生模块开发接口 N-API 做一次初步的尝试和解析,使得大家对 Node.js 原生 C++ 模块开发的固有印象(认为特别麻烦)有一个比较好的改观,让大家都来尝试一下 Node.js 原生 C++ 模块的开发。

  虽然 Node.js 原生 C++ 模块开发方式有了很大的改变,但是有一些内容是不变的,至少到现在来说都是基本上没什么 Breaking 的变化。

  这就要从 Node.js 最本质的 C++ 模块开发讲起了。举个例子,我们在 Linux 下有一个合法的原生模块ons.node,它其实是一个二进制文件,使用文本编辑器无法正常地看出什么鬼,直到我们遇到了二进制文件查看器。

  眼尖的同学会看到它的 Magic Number[^1] 是0x7F454C46,其按位的 ASCII 码代表的字符串是ELF。于是答案呼之欲出,这就是一个 Linux 下的动态链接库文件。

  事实上,不只是在 Linux 中。当一个 Node.js 的 C++ 模块在 OSX 下编译会得到一个后缀是*.node本质上是*.dylib的动态链接库;而在 Windows 下则会得到一个后缀是*.node本质上是*.dll的动态链接库。

  这货是 Node.js 中编译原生模块用的。自从 Node.js v0.8 之后,它就跟 Node.js 黏上了,在此之前它的默认编译帮助包是 node-waf[^3],对于老 Noder 来说应该不会陌生的。

  node-gyp 是基于 GYP[^4] 的。它会识别包或者项目中的binding.gyp[^5] 文件,然后根据该配置文件生成各系统下能进行编译的项目,如 Windows 下生成 Visual Studio 项目文件(*.sln等),Unix 下生成 Makefile。在生成这些项目文件之后,node-gyp 还能调用各系统的编译工具(如 GCC)来将项目进行编译,得到最后的动态链接库*.node文件。

  从上面的描述中大家可以看到,Windows 下编译 C++ 原生模块是依赖 Visual Studio 的,这就是为什么大家在安装一些 Node.js 包的时候会需要你事先安装好 Vusual Studio 了。

  关于 GYP 配置文件的更多内容,大家可自行去官方文档观摩,在脚注中有 GYP 的链接。

  node-gyp 除了自身是基于 GYP 的之外,它还做了一些额外的事情。首先,在我们编译一个 C++ 原生扩展的时候,它会去指定目录下(通常是~/.node-gyp目录下)搜我们当前 Node.js 版本的头文件和静态连接库文件,若不存在,它就会火急火燎跑去 Node.js 官网下载。

  这个头文件目录会在 node-gyp 进行编译时,以include_dirs字段的形式合并进我们事先写好的binding.gyp中,总而言之,这里面的所有头文件能被直接#include。

  node-gyp 是一个命令行的程序,在安装好后能通过$node-gyp直接运行它。它有一些子命令供大家使用。

  第 N 套国际 Node.js 开发者原生 C++ 模块开发方式,时代在召唤。

  除去前文中讲的一些不变的内容,还有很多内容是一直在变化的,秒速赛车是哪里的开奖虽然说用老旧的开发方式也是可以开发出能用的 C++ 原生模块,但是旧不如新。

  而且,其实目前来说 node-gyp 的地位也有可能在未来进行变化。因为当年 Chromium 是通过 GYP 来管理它的构建配置的,现如今已经步入了 GN[^6] 的殿堂,是否也意味着 node-gyp 有一天也会被可能叫做 node-gn 的东西给取代呢?

  在 Node.js 0.8 之前,通常在开发 C++ 原生模块的时候,是通过 node-waf 构建的。当然彼 node-waf 不是现在在 NPM 仓库上能搜到的 node-waf 了,当年那个 node-waf 早就年久失修了。

  不过就是因为有这个青黄交接的时候,那段时间的各种使用 C++ 来开发 Node.js 原生扩展的包为了兼容 0.8 前后版本的 Node.js,通常都是 binding.gyp 和 wscript 共存的。

  在早期的时候,Node.js 原生 C++ 模块开发方式是非常暴力的,直接使用其提供的原生模块开发头文件。

  举个最简单的例子,在几年前,你的 Node.js C++ 原生扩展代码可能是长这样的。

  这是一个最简单的echo函数,返回传进来的参数。写作 JavaScript 相当于是这样的。

  遗憾的是,这样的代码如果发成一个包,你现在是无论如何无法安装的,除非你用的是 0.10.x 的 Node.js 版本。

  为什么这么说呢,这段代码的确是在 Node.js 0.10.x 的时候可以用的。但是再往上升 Google V8 的大版本,这段代码就无法适用了,讲粗暴点就是没办法再编译通过了。

  事实上,根本不需要等到 6.x。上面的代码到 0.12 就已经无法再编译通过了。不只是函数声明的变化,连句柄作用域[^7]的声明方式都变了。

  如果要让它在 Node.js 6.x 下能编译,就需要改代码,就像这样。

  也就是说,以黑暗时代的方式进行 Node.js 原生模块开发的时候,一个版本只能支持特定几个版本的 Node.js,一旦 Node.js 的底层 API 以及 Google V8 的 API 发生变化,而这些原生模块又依赖了变化了的 API 的话,包就作废了。除非包的维护者去支持新版的 API,不过这样依赖,老版 Node.js 下就又无法编译通过新版的包了。

  总之在 NAN 出现之后,Node.js 的原生开发方式进入了城堡时代,并且一直持续到现在,甚至可能会持续到好久之后。

  说 NAN 是 Node.js 原生模块抽象接口可能还是有点抽象,那么讲明白点,它就是一堆宏判断。比如声明一个函数的时候,只需要通过下面的一个宏就可以了:

  NAN 的宏会判断当前编译时候的 Node.js 版本,根据不同版本的 Node.js 来展开不同的结果。这会儿就又会提到先前的两个函数声明对比了。

  而且 NAN 可不只是提供了NAN_METHOD一个宏,它还有一坨一坨数不清的宏供开发者使用。

  这样做的好处就是,代码只需要随着 NAN 的升级做改变就好,它会帮你兼容各不同 Node.js 版本,使其在任意版本都能被编译使用。

  即使是 NAN 这样的好物,也有自己的一个使命,使命之外的东西会被逐渐剥离。比如 0.10.x 和 0.12.x 等版本就应该要退出历史舞台了,NAN 会逐渐放弃对它们的兼容和支持。

  据官方文档所述,它的发音就是一个单独的N,加上 API,即四个英文字母单独发音。

  首先,我们知道,即使是在 NAN 的开发方式下,一次编写好的代码在不同版本的 Node.js 下也需要重新编译,否则版本不符的话 Node.js 无法正常载入一个 C++ 扩展。即一次编写,到处编译。

  而 N-API 相较于 NAPI 来说,它把 Node.js 的所有底层数据结构全部黑盒化,抽象成 N-API 当中的接口。

  不同版本的 Node.js 使用同样的接口,这些接口是稳定地 ABI 化的,即应用二进制接口(Application Binary Interface)。这使得在不同 Node.js 下,只要 ABI 的版本号一致,编译好的 C++ 扩展就可以直接使用,而不需要重新编译。事实上,在支持 N-API 接口的 Node.js 中,的确就指定了当前 Node.js 所使用的 ABI 版本。

  为了使得以后的 C++ 扩展开发、维护更方便,N-API 致力于以下的几个目标:

  而这些 API 主要就是用来创建和操作 JavaScript 的值了,我们就再也不用直接使用 Google V8 提供的数据类型了。毕竟在 NAN 中,就算我们有时候看不到 Google V8 的影子,实际上在宏展开后还是无数的 Google V8 数据结构。

  。任何 N-API 调用都返回一个napi_status枚举,来表示这次调用成功与否。N-API 的返回值由于被napi_status占坑了,所以真实返回值由传入的参数来继承,如传入一个指针让函数操作。所有 JavaScript 数据类型都被黑盒类型napi_value封装,不再是类似于v8::Object、v8::Number等类型。如果函数调用不成功,可以通过napi_get_last_error_info函数来获取最后一次出错的信息。

  注意:哪怕是现在的 Node.js v8.x 版本,N-API 仍处于一个实验状态,个人认为还有非常长的一段路要走,所以大家在生产环境中还不必太过于激进,不过 N-API 依然是大势所趋;不过对于使用老版本的 Node.js 开发者来说,大家也不要着急,即使 N-API 是在 v8.x 才正式集成进 Node.js,在其它旧版本的 Node.js 中依然可以将 N-API 作为外挂式的头文件[^9]中使用,只不过无法做到跨版本的特性,这只是它做的向后兼容的一个事情而已。

  关于 N-API 一系列的函数可以访问它的文档了解更多详情,现在我们来点料儿让大家对 N-API 的印象不是那么抽象。

  在封建时代和 NAN 所处的,模块的初始化是交给 Node.js 提供的宏来实现的。

  相应地,这个初始化函数Init的写法也会有所改变。比如这是封建时代和 NAN 时代的两种不同写法:

  那么上面Init函数中的desc意思就是,即将被挂在的对象下会挂一个叫echo的东西,它的函数是Echo,其它的getter、setter等全是空指针,而属性则是napi_default。

  在 N-API 中,你不用再被告知需要有 C++ 基础,C 即可。因为在 N-API 里面,声明一个Echo是这样的:

  重要:目前 8.0.0 和 8.1.0 版本的 Node.js 官方文档中,关于 N-API 的各种接口文档错误颇多,所以还是要以能使用的接口为准。

  而且现在大家也有很多人正在帮忙一起修复文档。例如现在的 JavaScript 函数声明返回值其实是napi_value,而官方文档上还是老旧的void。又比如napi_property_descriptor_desc结构体中,在utf8name之后还有一个napi_value的变量,而文档中却是没有的。

  这也是为什么我前面强调目前来说 N-API 还处于试验阶段。毕竟 API 并没有完全稳定下来,还处于一个快速迭代的步伐中,文档的更新并未跟上代码的更新。至少在笔者写作的当前是这样的(现在日期 2017 年 6 月 9 日)。

  通过napi_get_cb_info获取当次函数请求的参数信息,包括参数数量和参数体(参数体以napi_value的数组形式体现)。看看解析有无出错(status不等于napi_ok)或者看看参数数量是否小于 1。若解析出错或者参数数量小于 1,通过napi_throw_type_error在 JavaScript 层抛出一个错误对象,并返回。若无错则继续进行。返回argv[0],即第一个参数。Demo 完整代码

  首先在 Node.js v8.x 下进行试验,把这两段代码分别放到同一个目录下,命名好后,执行这样的终端命令:

  效果显而易见,在刚启动 Node.js REPL 的时候,你会得到一个警告。

  表示它目前还不是特别稳定,但是值得我们展望未来。然后在我们require()扩展的时候,我们就得到了一个拥有echo函数的对象了。

  我们尝试了三种调用方式。第一次是规规矩矩传入一个参数,echo如期返回我们传入的参数2333;第二次传入两个参数,echo返回了第一个参数蛋花汤:dog:;最后一次我们没传任何参数,这个时候就走到了 C++ 扩展中判断函数参数数量失败的条件分支,就抛出了一个Wrong number of arguments的错误对象。

  总之,它按照我们的预期跑起来了。并且代码里面并没有任何 Node.js 非 N-API 所暴露出来的数据结构和 V8 的数据结构--版本差异消除了。

  接下来激动人心的时刻到了,如果读者是使用nvm来管理自己的 Node.js 版本的话,可以尝试着安装一个 8.1.0 的 Node.js 版本。

  在安装成功切换版本成功后,尝试着直接打开 Node.js RELP,忘掉再次编译刚才编译好的扩展这一步。(不过别忘了--napi-module参数)

  把刚才用于测试的几句 JavaScript 代码再重复地输入--N-API 诚不我欺,居然还是能输出结果。这对于以前的暴力做法和 NAN 做法来说,无疑是非常大的一个进步。

  至此,我希望大家还没有忘记 N-API 是自 Node.js 8.0 之后出的特性。所以之前 Demo 的代码并不能在 Node.js 8.0 之前的版本如期编译和运行。

  先别急着摔。文中之前也说了,有一个外挂式头文件的包,其包名是node-addon-api。

  我们就试着通过它来进行向下兼容吧。首先在我们刚才的源码目录把这个包给安装上。

  还是由于快速迭代的原因,我不能保证这个包当前版本的时效性,不过我相信大家都有探索精神,在未来版本不符导致的 API 不符的问题应该都能解决。

  然后,给我们的binding.gyp函数加点料,加两个字段,里面是两个指令展开。

  有了这两个字段后,就表示我们依赖了外挂式 N-API 头文件。而且它内部自带判断,如果版本已经达到了有 N-API 的要求,它的依赖就会是一个空依赖,即不依赖外挂式 N-API 编译的静态连接库。

  至于源码层面,我们就不需要作任何修改。在 Node.js v6.x 下面试试看吧。同样是使用node-gyp rebuild进行编译。然后通过 Node.js REPL 进去测试。

  具体的终端输出这里就不放出来了,相信经过实验的大家都得到了自己想要的结果。

  本次内容主要讲解了在 Node.js 领域中原生 C++ 模块开发的方式变迁。

  目前的中坚力量仍然是 NAN 的开发方式,甚至我猜测是否未来有可能 NAN 会提供关于 N-API 的各种宏封装,使其彻底消除版本差异,包括 ABI 版本上的差异。当然这种 ABI 版本差异导致的需要多次编译问题应该还是存在的,这里指的是一次编码的差异。

  在大家跟着本文对 N-API 进行了一次浅尝辄止的尝试之后,希望能对当下仍然处于实验状态的 N-API 充满了希冀,并对现在存在的各种坑处以包容的心态。

  全称 Generate Your Projects,是谷歌开发的一套构建系统,未尽事宜详询 。GYP 的配置文件的后缀就是

  等,是个类 JSON 文件。GN 是谷歌开发的相较于 GYP 更新更快的一套构建工具,可以参考让垃圾回收机制来管理 JavaScript 对象生命周期的一种类,即HandleScope,在我的新书中将会有详解。Node.js 的异步事件循环支撑者,详询详情请查看 node-api 这个包,

版权保护: 本文由 主页 原创,转载请保留链接: http://www.ecentiv.com//html/675.html