从Node 8的N-Api开始,讲解Rust绑定的2种方式,最后给出作为Node程序员,如何快速学习Rust!
自从Node.js 8版本出来之后,N-API是一个亮点,即编写一次,在多个版本里使用。跟跨平台类似,它是跨版本。这解决了以前一直困扰Node的addon每次都需要编译的问题。尤其是像sass这种c写的模块
Error: The module 'node-sass'
was compiled against a different Node.js version using
NODE_MODULE_VERSION 51. This version of Node.js requires
NODE_MODULE_VERSION 55. Please try re-compiling or re-installing
the module (for instance, using `npm rebuild` or `npm install`).
NODE_MODULE_VERSION 是每一个 Node.js 版本内人为设定的数值,意思为 ABI 的版本号。一旦这个号码与已经编译好的二进制模块的号码不符,便判断为 ABI 不兼容,需要用户重新编译。
这其实是一个工程难题,亦即 Node.js 上游的代码变化如何最小地降低对 C++ 模块的影响,从而维持一个良好的向下兼容的模块生态系统。最坏的情况下,每次发布 Node.js 新版本,因为 API 的变化,C++ 模块的作者都要修改它们的源代码,而那些不再有人维护或作者失联的老模块就会无法继续使用,在作者修改代码之前社区就失去了这些模块的可用性。其次坏的情况是,每次发布 Node.js 新版本,虽然 API 保持兼容使得 C++ 模块的作者不需要修改他们的代码,但 ABI 的变化导致必须这些模块必须重新编译。而最好的情况就是,Node.js 新版本发布后,所有已编译的 C++ 模块可以继续正常工作,完全不需要任何人工干预。
Node.js 8 的 Node.js API (N-API) 就是为了解决这个问题,做到上述最好的情况,为 Node.js 模块生态系统的长期发展铺平道路。N-API 追求以下目标:
N-API 采取以下手段达到上述目标:
N-API 目前在 Node.js 8 仍是实验阶段的功能,需要配合命令行参数 --napi-modules 使用。
目前有3个分支,2个内核都已经支持N-API了
更多文档
https://github.com/jupp0r/node-api
作者说使用rust绑定n-api的3点好处
Node虽好,但同步和cpu密集运算一直是痛点,但他们可以在Native模块里解决,这些恰恰是rust这种系统级的语言擅长的,它比go的性能还要出色。
Cargo是Rust的包管理工具,我们直接看它的配置文件 https://github.com/jupp0r/node-api/blob/master/Cargo.toml
[package]
name = "node-api"
version = "0.1.0"
authors = ["Jupp Müller <jupp0r@gmail.com>"]
[lib]
crate-type = ["lib"]
[dependencies]
node-api-sys = "0"
futures = "0.1"
依赖了2个模块
至此,大家应该会理解,NAPI解决Rust的binding问题,利用Stream流机制,Node和Rust可以零成本消耗对接,是不是想想就美好?
io密集的node擅长,cpu密集的rust擅长,强强联合呢?
这是菜神fundon跟我提起的,说 https://github.com/neon-bindings/neon 是相当不错的库。用Rust开发native模块。既然是抽象层,对老版本的addon和N-API兼容应该都好做。
文档里讲为什么选择Rust
用法,安装命令行工具
npm install -g neon-cli
新建项目
neon new hello-node
目录如下
hello-node/
├── README.md
├── lib/
│ └── index.js
├── native/
│ ├── Cargo.toml
│ └── src/
│ └── lib.rs
└── package.json
代码
#[macro_use]
extern crate neon;
use neon::vm::{Call, JsResult};
use neon::js::JsString;
fn hello(call: Call) -> JsResult<JsString> {
let scope = call.scope;
Ok(JsString::new(scope, "hello node").unwrap())
}
register_module!(m, {
m.export("hello", hello)
});
很简单吧?如果大家感兴趣,不妨看看更复杂的例子,https://github.com/dherman/wc-demo。
整体来说,neon这种采用rust + node绑定的方式,开发简单,性能高,充分发挥2门语言的优势,取长补短,应该是最实惠的组合。
作为Node程序员,当然对比node和rust学习是最简单,这里推荐 https://github.com/Mercateo/rust-for-node-developers
已有7章,入门还是相当不错的
简单对比一个
Rust :
fn main() {
println!("Hello World!");
}
JavaScript
function main() {
console.log('Hello World!');
}
Rust
extern crate hyper;
use std::io::Read;
use hyper::Client;
fn main() {
let url = "https://api.github.com/users/donaldpipowitch";
let client = Client::new();
let mut res = client.get(url).send().expect("Couldn't send request.");
let mut buf = String::new();
res.read_to_string(&mut buf).expect("Couldn't read response.");
println!("Response: {}", buf);
}
JavaScript
import { get } from 'https';
const host = 'api.github.com';
const path = '/users/donaldpipowitch';
get({ host, path }, (res) => {
let buf = '';
res.on('data', (chunk) => buf = buf + chunk);
res.on('end', () => console.log(`Response: ${buf}`));
}).on('error', (err) => { throw `Couldn't send request.` });
推荐一些好的Rust学习资料
当然也有视频
Node开发简单,在io密集操作方面有着特有优势,尤其是N-Api出来之后,对于c/c++扩展的支持更好,再也不担心跨版本开发的问题了。而Rust作为系统语言,在高并发,cpu密集任务方面有着更优秀的表现。本文中的2种方法,都可以看出,使用Rust作为Node.js的native模块开发的有点,简单,高效,实用。
在未来,这应该会是一种流行趋势,在大潮的我们,有必要去学习一下。