Node Loves Rust
发布于 8 年前 作者 i5ting 14695 次浏览 来自 分享

Node Love Rust

从Node 8的N-Api开始,讲解Rust绑定的2种方式,最后给出作为Node程序员,如何快速学习Rust!

images.png

N-API说明

自从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 追求以下目标:

  1. 有稳定的 ABI
  2. 抽象消除 Node.js 版本之间的接口差异
  3. 抽象消除 V8 版本之间的接口差异
  4. 抽象消除 V8 与其他 JS 引擎(如 ChakraCore)之间的接口差异

N-API 采取以下手段达到上述目标:

  1. 采用 C 语言头文件而不是 C++,消除 Name Mangling 以便最小化一个稳定的 ABI 接口
  2. 不使用 V8 的任何数据类型,所有 JavaScript 数据类型变成了不透明的 napi_value
  3. 重新设计了异常管理 API,所有 N-API 都返回 napi_status,通过统一的手段处理异常
  4. 重新了设计对象的生命周期 API,通过 napi_open_handle_scope 等 API 替代了 v8 的 Scope 设计

N-API 目前在 Node.js 8 仍是实验阶段的功能,需要配合命令行参数 --napi-modules 使用。

目前有3个分支,2个内核都已经支持N-API了

  • api-prototype-6.2.0
  • api-prototype-8.x
  • api-prototype-chakracore-8.x

更多文档

Rust对N-API的封装

https://github.com/jupp0r/node-api

作者说使用rust绑定n-api的3点好处

  • n-api是纯的plain c,故而Rust bindings更简单
  • n-api是稳定的,ABI可以跨版本兼容,对于模块维护非常友好
  • API是独立的,可以跨V8、ChakraCore, SpiderMonkey,以及未来更多的平台

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擅长,强强联合呢?

Rust抽象层neon

这是菜神fundon跟我提起的,说 https://github.com/neon-bindings/neon 是相当不错的库。用Rust开发native模块。既然是抽象层,对老版本的addon和N-API兼容应该都好做。

文档里讲为什么选择Rust

  • 高性能: While V8 is a high-quality modern JavaScript engine, it is generally much easier to get higher performance, and with greater predictability, from Rust than from high-level dynamic languages like JavaScript.
  • 高并发: Neon provides access to Rust’s fearless concurrency and parallelism, which allows you to take advantage of modern multicore hardware when operating on certain types of JavaScript data.
  • 绑定简单: Neon is a great way to connect the NPM and Cargo ecosystems to make packages built for one available to the other.

用法,安装命令行工具

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门语言的优势,取长补短,应该是最实惠的组合。

如何学Rust

作为Node程序员,当然对比node和rust学习是最简单,这里推荐 https://github.com/Mercateo/rust-for-node-developers

已有7章,入门还是相当不错的

  1. Setup
  2. Hello World
  3. Package Manager
  4. Read files
  5. Write files
  6. HTTP requests
  7. Parse JSON

简单对比一个

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学习资料

  • The Rust Book: will teach you about the Rust Programming Language.
  • Rustlings: small exercises for learning Rust
  • into_rust(): a series of short video tutorials
  • Rust by Example: a collection of runnable examples that illustrate various Rust concepts

当然也有视频

总结

Node开发简单,在io密集操作方面有着特有优势,尤其是N-Api出来之后,对于c/c++扩展的支持更好,再也不担心跨版本开发的问题了。而Rust作为系统语言,在高并发,cpu密集任务方面有着更优秀的表现。本文中的2种方法,都可以看出,使用Rust作为Node.js的native模块开发的有点,简单,高效,实用。

在未来,这应该会是一种流行趋势,在大潮的我们,有必要去学习一下。

20 回复

前端之巅

(ID:frontshow)

每周清单,带你跟上前端发展;巅峰分享,带你学习一线经验;技术社群,给你纯净交流平台!前端之巅,关注前端发展,共享一线技术,三万淀粉等你一起来攀登!

前端之巅.jpg

StuQ

(ID:StuQ2015)

斯达克学院(StuQ ),程序员交流、互动、分享的最大社区。在这里,有一线最强技术大牛做你的导师;在这里,有IT人最有效的进阶方式。

StuQ

大数据杂谈

(ID:BigdataTina2016)

我们专注大数据和机器学习,每天发布高质量文章,技术案例等原创干货源源不断。更有社群微课堂,也希望你能从这里分享前沿技术,交流深度思考。

大数据杂谈

移动开发前线

(ID:bornmobile)

每天分享最前沿和第一线移动开发技术。做更好的技术分享,介绍一线互联网公司的移动技术实践,让你与时代保持同步,消除信息焦虑。我们还建了移动前线学习分享群,让更多的人参与分享,让你的分享被更多人看到,让学习与分享的门槛更低。

移动开发前线

思特沃克

(ID:ThoughtWorks)

这里汇集了一群有洞见、有态度的技术人,与你分享来自一线的实战经验,洞察构建未来的技术趋势。

思特沃克

Phodal

(ID: phodal-weixin)

Phodal,知名 md 工程师(GitHub 中国区 Top 20 程序员、InfoQ 编辑 、CSDN 前端专家,《自己动手设计物联网》和《全栈应用开发:精益实践》作者)创建的公众号。公号主要内容以全栈、前端、程序员人生及秀恩爱为主。

Phodal

Node全栈

(ID: nodeonly)

简介:CNodejs社区官方公众号,关注Node.js、全栈,以及以js为核心的大前端,开源,架构方向,不定期推送精品文章,所有文章都会出现在cnode社区的帖子里。

Node全栈

奇舞周刊

领略前端技术 阅读奇舞周刊~

奇舞周刊

高效开发运维

(ID:DevOpsGeek)

InfoQ运维领域垂直号。常规运维、亦或是崛起的DevOps,探讨如何IT交付实现价值。努力为技术人呈现有实践意义的内容~

高效开发运维

前排鼓掌👍👍

来自酷炫的 CNodeMD

@i5ting 用node-java是不是损耗比rust较大

来自酷炫的 CNodeMD

@dbit-xia 很少有人这么干

@i5ting 😓我就这样干的,只能是尽量少交互

来自酷炫的 CNodeMD

@dbit-xia 求分享啊,让我们学学

@i5ting

  • node-java demo 效率还是比较理想Ծ‸Ծ windows安装比较麻烦

来自酷炫的 CNodeMD

@dbit-xia 整理篇带经验的文章啊,加油,你可以的

厉害啦. 我的🐺 自豪地采用 CNodeJS ionic

不错,这段时间正准备学习一下

rust 扩展和 c++ 扩展相比有哪些优劣势呢?

@hyj1991 写个for循环从1加到10亿,rust只需要0纳秒,c++需要200ms 你看rust生成的汇编代码 untitled1.png 汇编就直接给你算好了 当然还是极少数,无GC语言,编译器完成内存管理工作,很开阔编程视野

@zy445566 这感觉是静态编译阶段就计算好了,但是如果是运行时进行的复杂计算,在同等条件下和 c/c++ 的计算效率比结果怎么样呢?

@hyj1991 http://benchmarksgame.alioth.debian.org/u64q/rust.html 是不是复杂用例,不知道。有的快,有的慢,毕竟少了GC的时间 还有rust分两种编译,一是不编译优化,给我们debug用的,另一种是release,就是你看到汇编(正式花时间久) 学这个主要不是追求性能吧,我觉得主要还是开阔编程视野

@zy445566 挺不错的,扩展能有多一个选择是好事

谢谢大神分享,go 跟 rust 两种语言都很有吸引力啊。

@hyj1991 最主要的是对node友好,这点rust比go好太多了

@i5ting 这倒是,值得花点时间研究下

@i5ting 期待你来一个具体使用的教程!:)

回到顶部