nodejs 拓展 C++ 插件
前言
现代客户端开发模式正处于一个飞速的变革,从一开始纯粹的 C 端, 逐渐演化成 C/S 的模式,在到当今比较激进的 B/S 模式.
Electron
给客户端带来了太多了变革; 在 Electron
的加持下, 客户端的开发模式也逐渐向后台开发靠拢, 即使用 Electron
内嵌 HTML
快速完成页面的开发,而将具体的功能实现(服务)交给了具体后台语言.
目前主流的客户端开发语言仍然是 C/C++
, 在 Windows 下可能也存在着 C#
; 不过由于 nodejs
对于前端的友好性,即其语法风格与 javascript
十分相似, 给予前端同学全栈开发的可能, 所以以 nodejs
为代表的轻代码全栈开发逐渐流行了起来.
nodejs
足以满足大多数的客户端需求, 大量的业务逻辑被转移至 nodejs
处理, 甚至在某些场景下整个软件的 “后台” 可能完全由 nodejs
; 作为轻代码的典范,nodejs
能做得很快, 但稍微复杂点的功能对于 nodejs
而言可能就没有办法了; 所以将部分的 C/C++
代码封装成 nodejs
插件 (类似于 java
中的 JNI
)就显得十分重要, 通过 C/C++
拓展 nodejs
的基础功能,一方面能充分利用到开发过程中 nodejs
的高效性,另一方面则能顾及到 nodejs
的泛用性,以及运行中的效率性.
Nodejs 插件原理
在阅读此节之前,强烈建议 从暴力到 NAN 再到 NAPI——Node.js 原生模块开发方式变迁.
插件 一词并不是只存在于 nodejs
中,插件的核心在于可插拔,可替换; 实际上许多优秀的 C/C++
项目都实现了插件的支持,例如在音视频领域中的 VLC
(播放器) 以及 OBS
(推流工具) 都实现了插件的机制.
无关于语言,目前的插件实现大致上分为两种,静态插件以及动态插件; 静态插件的实现大多是 SDK
提供了一个友好的抽象接口,开发者根据其规范将其实现并将其实现代码嵌入SDK源码中,随 SDK
一起构建; 而动态插件则大同小异地通过类似于 dlopen
类似的机制,利用 C/C++
动态库运行时加载的特性,通过一些配置的方法(如简单的 json
配置).静态插件和动态插件都需要一定强规范来插件接口的协议做具体的定义, 不同于静态插件, 动态插件实现了更加灵活的选择方案.
目前插件文化,或者说服务化的趋势逐渐增强; java 已经出现了专门管理这种插件的管理机制,如
OSGi
; C++ 如 macchina.io中推崇的 OSP(Open Service Platform).
nodejs
的插件也无外乎上面的插件实现方案,在 从暴力到 NAN 再到 NAPI——Node.js 原生模块开发方式变迁 一文中,给出的 nodejs
插件的三个不同的实现阶段:
- 封建时代
- 城堡时代(NAN)
- 帝国时代(N-API)
封建时代 插件开发者直接引用 V8 内核代码, 偏向于 静态插件 的概念 (实际上还是动态插件); 而 城堡时代 和 帝国时代 更偏向于 动态插件的概念, 不同的是 城堡时代 时更多的是通过 NAN
使用大批量的宏去适应不同版本的 nodejs
, 而 帝国时代 则是通过 nodejs
定义了一套强制性的接口实现规范.
在 封建时代, 插件开发者苦恼于浏览器内核的更新,一旦浏览器内核更新,则之前的嵌入方式就发生的改变,插件适配人员需要重新进行适配; 而 城堡时代 对于插件开发者来说不同版本的 nodejs
的差异性被磨平了, 但是对于插件使用者而言却不是, 城堡时代 归根结底只是使用大量的宏以及条件判断去主动适配不同版本的 nodejs
,所有对于插件使用者而言必须使用对应版本的 nodejs
重新进行源码编译; 而 帝国时代 做到了真正意义上的大一统,通过对 nodejs
本身制定插件标准, 能够使 nodejs
插件彻底独立于 nodejs
版本,甚至于 V8
内核.
目前 NAN
的插件方式仍然是主流,不过随着时代的发展,N-API
一定会成为未来 nodejs
插件开发的唯一方式.
插件构建
对于大部分的开发者而言, C/C++
的构建方式大多还是熟悉于 Makefile
或者 cmake
; 而实际上 C/C++
的构建方式很多种,比如说系统构建常见的有 meason
, 而对于浏览器而言,谷歌自己搞了一套 gyp
(不过谷歌好像在渐渐地弃用 gyp
).
而为了将插件的构建融入 npm
中去,基于 gyp
又演化出了 node-gyp
的方式.
目前插件的构建方式主要有两种,前者是比较普遍的,后者则是比较小众的:
- node-gyp : 基于谷歌的
gyp
- cmake-js : 基于
cmake
关于如何使用 cmake-js
构建插件,请参考示例.
插件标准
以 N-API
的方式进行插件开发,在开发前需要阅读 Node.js N-API.
NAN
方式的插件开发方案则参考 bsdiff(node-gyp) 的实现.
示例
该 示例 基于 bsdiff(node-gyp), 将其修改为以 cmake-js
进行构建的方式,同时给出了以原生 cmake
的方式进行项目构建;与此同时给出了交叉编译 nodejs C++ 插件的方式.
该示例基于本文中 NAN
的方式进行插件拓展.