简介: 来一起为 Node.js 的 add-on 生态做贡献吧~

作者 | 吴成忠(昭朗)

这篇文章是由 Chengzhong Wu (@legendecas),Gabriel Schulhof (@gabrielschulhof) ,Jim Schlight (@jimschlight),Kevin Eady,Michael Dawson (@mhdawson1),Nicola Del Gobbo (@NickNaso) 等人编写的,首发在 Node.js Medium 博客。

关于N-API

N-API 为 Node.js 带来了一个 ABI 稳定的 add-on API,简化了构建和开发支持跨 Node.js 版本的 add-on 的负担。

目前 N-API 的 C++ 封装 node-addon-api 每周的下载量已经超过了 250万次,并且所有 Node.js LTS(长期支持版本)都已经支持了 N-API v3 或者更高版本 ,Node.js 15.x 更已经开始支持最新的 N-API v7。所以我们认为这是一个非常好的时间点来回头看一看目前 Node.js add-on 的开发体验。

当我们在 2016 年开始投入 N-API 的工作(最开始的提案是在 2016 年 12 月 12 日提出的),我们就知道这会是一个非常长期的任务。Node.js 社区生态中已经有非常多现存的包,所以这个迁移过程将会持续相当长的一段时间。

不过好消息是,从最初的想法,到现在这段路程我们已经走过了非常长的路途。许许多多的困难已经由多位 Node.js Collaborator、N-API 团队和模块包作者们攻克。目前,N-API 已经成为了默认、推荐的编写 Node.js add-on 的方式。

随着 N-API 的发展,不断有新的 API 加入到 N-API 中去来满足 Node.js 模块包作者将他们的库向 N-API 迁移中提出新需求,当然这个过程也按照我们预先的设计 N-API 一直保持着稳定、向前兼容性。

我们也非常高兴地看到这些模块包作者们的积极反馈,比如 https://twitter.com/mafintosh/status/1256180505210433541

不多说,我们先来看看过去几年被添加到 N-API 中的新特性吧。

新特性

越来越多的开发者们开始使用 N-API 与 node-addon-api 开发 Node.js add-on,我们也不断地为 N-API 和 node-addon-api 添加新的关键特性和改进 add-on 开发体验。

这些改进可以分为 3 个主要的类别,我们下文将一一介绍。

多线程与异步编程

随着 Node.js 的使用在开发者群体中越来越显著,需要与 OS 接口、异步事件打交道的需求也越来越旺盛。Node.js 是一个 JavaScript 单线程模型的实现,一个 Node.js 环境只会有一个主线程可以访问 JavaScript 值。

因此,在主线程执行重 CPU 的任务就会导致 JavaScript 程序被阻塞,导致事件与回调都堆积在事件队列中。为了改进程序的跨线程数据完整性的开发体验,我们收集了非常多的真实案例的需求,在 N-API 和 N-API 的 C++ 封装 node-addon-api 中都带来了多种机制来解决工作线程回调回 JavaScript 线程的问题。根据使用场景,可以分为:

  • AsyncWorker,提供单向、单次的回调任务封装,可以通知 JavaScript 这个任务的最终执行结果或者异常信息;
  • AsyncProgressWorker,与 AsyncWorker 类似,提供单向、单次的回调任务封装,不过增加了向 JavaScript 异步传递进度信息的机制;
  • Thread-safe functions,提供了从任意线程、任意数量的线程、任意时间点向 Node.js JavaScript 线程回调的机制。

多 Node.js 上下文支持

Node.js 近期最让人兴奋的特性之一就是 [worker_threads],它提供了一个完整的、但是独立于 Node.js 主 JavaScript 线程的并发执行的 Node.js JavaScript 执行线程。这也意味着 Node.js 的 add-on 也同样可以在这些 worker 线程中随着这些 worker 的启动与销毁被多次加载、卸载。

不过因为这些同一个进程中的 worker 线程是共享了同一个内存空间的,多个 add-on 的实例必须考虑到多个 worker 线程的同时存在的可能性。另外,每一个 Node.js 进程只会加载了一次这些 add-on 的动态库,这意味着这些 add-on 线程不安全的全局属性(比如全局静态变量)可以被多个线程同时访问,也就不能再这么简单粗暴地存储了。

类似的,C++ 类的静态数据成员也是通过线程不安全的方式存储的,所以这个方式也需要被避免。另外,其实对于 add-on 来说,Node.js 也不保证单个线程只会用来执行一个 worker,所以 thread-local 也应该被避免。

在 N-API v6 中,我们为每一个 Node.js 实例(主线程 JavaScript 实例、worker 实例等)都引入了一个用来给 add-on 使用的存储空间。这样,add-on 在一个进程中就可以获得对于单个 Node.js 实例唯一的存储空间了。同时我们也提供了一些辅助方法来帮助 add-on 开始使用这个特性:

  • NAPI_MODULE_INIT() 宏,会将 add-on 标记为可以被 Node.js 在同一个进程中可以多次加载、卸载的模块。
  • napi_get_instance_data() 和 napi_set_instance_data() 用来安全地访问单个 Node.js 实例给 add-on 创建的全局唯一存储空间;
  • node-addon-api 还提供了 Addon<T> 类,这个类包装了上面说所的方法,以 C++ 友好的方式封装了这个给予 add-on 可以在不同的 worker 线程中使用的存储空间。因此,add-on 开发者可以将 add-on 的数据比如全局变量通过 Addon<T> 来存储并创建,而 Node.js 则会负责在当前线程使用这个 add-on 的时候创建这片空间。

其他辅助函数

除了以上几个重要功能之外,我们也发现了许多在维护 Node.js add-on 的过程中经常会使用到的类型方法与函数,包括:

  • Date 对象;
  • BigInts;
  • 从 JavaScript 对象上获取任意键(如 Symbol 等);
  • 将 Add-on 创建的 ArrayBuffer 底层存储从 ArrayBuffer 上脱离;

构建

构建工作流对于 Node.js add-on 维护者与 add-on 使用者来说是非常重要的一个环节,也是N-API 团队其中一个工作重心,比如 CMake.js, node-pre-gyp 和 prebuild。

曾经 Node.js add-on 只能使用 node-gyp 来构建。对于一些已经在使用 CMake 的库来说,CMake.js 就是除了 node-gyp 依赖用来构建 add-on 的一个非常吸引人的选项。我们也已经发布了一个使用 CMake 构建 add-on 的例子。

其他关于如何将 CMake.js 与 N-API add-on 一起使用的详细信息可以在 N-API Resource 获取到。

开发 Node.js add-on 之后一个重要的现实问题就是在 npm install 时,add-on 的 C/C++ 代码必须在本地编译、链接。这个编译过程需要本地安装有一个可以正常使用的 C/C++ 工具链。而这个依赖通常会成为没有安装这些工具链的 add-on 用户使用这个 add-on 的一个阻碍。现行的方案对于这个问题一般都是预先构建二进制包,然后在安装时直接下载这些预先构建的包。

有许多工具可以用来预先构建二进制包。node-pre-gyp 通常会将构建出来的二进制包上传到 AWS S3。prebuild 也类似,不过是将包上传到 GitHub Release。

prebuildify 则是另外一个可选项。而 prebuildify 相比于上述的工具来说,优点在于在 npm install 安装好时,本地就已经有这些二进制包了,而不需要再次从第三方服务上下载。虽然安装的 npm 包可能会更大,不过在实际实践中因为不需要再次从 AWS 或者 GitHub 上下载,整个安装过程会相对更加快速。

开始上手

我们已经在 GitHub 上准备了非常多的 node-addon-examples 来给开发者快速了解常见场景该如何使用 N-API 和 node-addon-api 来开发 Node.js add-on。这个仓库的根目录包含了许多的文件夹,这些文件夹就代表了不同的使用场景,比如从简单的 Hello World add-on,到复杂的多线程 add-on。每一个样例目录会包含 3 个子目录,分别代表了传统的 NAN,N-API,和 node-addon-api 开发 add-on 的例子。我们可以直接运行下面的命令,立刻从 Hello World 的例子开始使用 node-addon-api:

$ git clone https://github.com/nodejs/node-addon-examples.git
$ cd node-addon-examples/1_hello_world/node-addon-api/
$ npm i
$ node .

另一个重要的资源就是 N-API Resource。这个网站包含了开发、构建 Node.js add-on 的从入门到深入的许多信息与资料,比如

  • 上手所需的工具;
  • 从 NAN 向 N-API 的迁移导引;
  • 不同构建系统的对比(node-gyp,CMake 等等);
  • 多 Node.js 上下文支持和线程安全。

结语

从 Node.js 诞生之初,Node.js 就支持通过 C/C++ 代码来给 JavaScript 暴露更多的特性接口。随着时间积累,我们也认识到实现、维护、分发这些 add-on 一直存在许许多多的难点。而 N-API 就被 add-on 维护者们认为是解决这些难点的一个非常核心的领域。所以整个N-API 团队和社区都开始为 Node.js 核心建立起这样一套 ABI 稳定的 add-on API。

而代表了 N-API 的这些 C API 现在已经是每一个 Node.js 发布版本的一部分,并且我们也有了可以通过 npm 安装的 node-addon-api 来提供这些 C API 的 C++ 封装。N-API 在诞生之初,就是以在不同 Node.js 版本之间,甚至是 Major 版本之间保证 ABI 与 API 兼容性为目标,而这也已经可以证明能够提供更多额外的好处:

  • 我们不再需要在切换 Node.js 大版本之后重新编译 add-on 模块;
  • 我们可以在除了使用 V8 作为 JavaScript 引擎的 Node.js 之外的运行环境实现 N-API,也意味着这些为 Node.js 开发的 add-on 无需修改任何代码即可兼容这些运行环境,比如 Babylon Native,IoT.js 和 Electron。
  • N-API 是单纯的 C API,这意味着我们可以使用 C/C++ 之外的语言、运行时开发 Node.js add-on,比如 Go 或者是 Rust。

N-API 从 Node.js v8.0.0 开始以实验性功能发布到现在,虽然广泛应用的过程比较缓慢,但是模块开发者们也不断地给我们提交反馈与贡献,这也帮助我们不断地增加新特性和开发新的工具来帮助开发者们构建一个更好的 add-on 生态。

今天,N-API 在 add-on 的开发中使用已经非常广泛。比如一些使用非常多的 add-on 模块都已经迁移至基于 N-API 开发:

  • sharp (每周 ~900k 下载量)
  • bcrypt (每周 ~500k 下载量)
  • sqlite3 (每周 ~300k 下载量)

在过去的几年中,N-API 获得了非常多的改进。而对于 add-on 开发者与用户来说,这也给他们带来了接近于原生 JavaScript 模块的开发、使用体验。

开始贡献

我们在持续不断地改进 N-API 和 Node.js 的 add-on 生态,但是我们也一直非常需要帮助。你可以在以下途径在多种场景帮助 N-API 做的更好:

  • 将你的 add-on 迁移到 N-API;
  • 帮助你的应用依赖的 add-on 迁移到 N-API;
  • 为 N-API 提出、实现新的特性;
  • 为 node-addon-api 提出、实现新的基于 N-API 的特性;
  • 为 node-addon-api 修复问题、增加测试用例;
  • 为 node-addon-examples 修复问题、增加测试用例;

原文链接
本文为阿里云原创内容,未经允许不得转载。

如何开发 Node.js Native Add-on?相关推荐

  1. Rust: 基于 napi-rs 开发 Node.js 原生模块

    Rust: 基于 napi-rs 开发 Node.js 原生模块 文章目录 Rust: 基于 napi-rs 开发 Node.js 原生模块 完整代码示例 背景 & napi 环境/工具链准备 ...

  2. 零基础开发 Node.js Addons 插件:参数与返回值处理

    上一篇回顾 零基础开发 Node.js Addons 插件:Hello Node-API.本篇介绍使用 Node-API 为 Node.js 开发基于 C 的 Addons 时,如何接收与处理 Nod ...

  3. 在Visual Studio上开发Node.js程序

    在Visual Studio上开发Node.js程序 原文:在Visual Studio上开发Node.js程序 [题外话] 最近准备用Node.js做些东西,于是找找看能否有Visual Studi ...

  4. 在Visual Studio上开发Node.js程序(2)——远程调试及发布到Azure

    [题外话] 上次介绍了VS上开发Node.js的插件Node.js Tools for Visual Studio(NTVS),其提供了非常方便的开发和调试功能,当然很多情况下由于平台限制等原因需要在 ...

  5. JavaScript之后端Web服务器开发Node.JS基本模块学习篇

    JavaScript之后端Web服务器开发Node.JS基本模块学习篇 基本模块 fs文件系统模块 stream支持流模块 http crypto加密模块 基本模块 因为Node.js是运行在服务区端 ...

  6. 使用FortJs使用现代JavaScript开发Node.js

    介绍 (Introduction) Nodejs gives you the power to write server side code using JavaScript. In fact, it ...

  7. 【Node.js】2.开发Node.js选择哪个IDE 开发工具呢

    安装完Node.js之后,就要为它选择一个有利的IDE用于开发. 相比较了多个IDE之后,定位在webstrom和sublime上. 有一个简单的比较: webstorm功能很丰富,前端开发工具的集大 ...

  8. Node.js模块化开发||Node.js中模块化开发规范

    JavaScript开发弊端 a.js b.js JavaScript在使用时存在两大问题,文件依赖和命名冲突. 生活中的模块化开发 软件中的模块化开发 app.j user.一个功能就是一个模块,多 ...

  9. php微信墙开发,Node.js如何开发微信墙

    这次给大家带来Node.js如何开发微信墙,Node.js开发微信墙的注意事项有哪些,下面就是实战案例,一起来看一下. 验证服务器有效性 接收用户通过微信订阅号发给服务器的消息 解析收到的XML文本消 ...

最新文章

  1. 特征工程学习,19项实践Tips!代码已开源!
  2. 蓝桥杯练习系统习题解答-入门训练
  3. laravel框架数据迁移
  4. 批量查询,mget语法,mget批量查询(来自学习资料,第26节)
  5. Linux永久挂载新的硬盘
  6. 与微软.NET创始人Scott Guthrie面对面,“红衣教主”亲身传授推动成功转型的洪荒之力
  7. LSP(分层服务提供程序)
  8. jbutton添加点击事件_electron-vue自定义边框后点击事件失效问题
  9. 多个 本地仓库_【运维工具】搭建npm私有镜像仓库,天下苦于npm build久矣
  10. Java经典实例:处理单个字符串
  11. 数据 3 分钟 | 阿里云数据库 2020 技术年报发布、TiDB 开启 Hacking Camp、达梦云数据库免费体验...
  12. 舒服的网页登录界面设计灵感
  13. 随想录(大学给了我们什么)
  14. GDB中打印pthread_internal_t的方法
  15. Python(七):输入输出(IO)、文件读写
  16. AI(Adobe illustrator)修改所有字体
  17. 《Gpu Gems》《Gpu Pro》《Gpu Zen》系列读书笔记
  18. gprs无线模块与服务器连接,GPRS 模块如何通信_GPRS 模块与服务器通信【原理解析】...
  19. 历史文化名城盛开数字化之花
  20. 为什么机器学习之路没有捷径可走

热门文章

  1. JAVA入门级教学之(IDEA工具的快捷键和简单设置)
  2. python pdf转txt保留全部信息_Python 将pdf转换成txt(不处理图片)
  3. numberformate php_php number_format函数怎么用?
  4. 谈谈Java与大数据之间的关系你们都了解了清楚了吗?
  5. 商城html源码_Java开源商城源码推荐,从菜鸡到大神,永远绕不开的商城系统
  6. 计量经济学计算机输出结果,计量经济学作业答案A..doc
  7. 【LeetCode笔记】199. 二叉树的右视图(Java、二叉树、层序遍历)
  8. android 访问服务器sql_XSS 攻击、CSRF 攻击、SQL 注入、流量劫持(DNS 劫持、HTTP 劫持)—— 浏览器安全
  9. leetcode37 --- solveSudoku(解数独)
  10. python selenium iframe怎么定位_python+selenium 切换iframe