用 Rust 和 Node-API 开发高性能 Node.js 模块

如果无法正常显示,请先停止浏览器的去广告插件。
分享至:
1. 第 十 六 届 D 2 前 端 技 术 论 坛 用 Rust 和 Node-API 开发高性能 Node.js 模块 龙逸楠
2. Node.js 中的 Native Addon Rust 在前端/Node.js NAPI-RS 介绍 目录 Rust 与 Node 的未来展望
3. Node.js 中的 Native Addon
4. Node.js 中的 Native Addon https://xcoder.in/2017/07/01/nodejs-addon-history/ Native Addon 的本质 • .node 格式的文件 • 是二进制文件 • 是一个动态链接库 (Windows DLL/Unix dylib/Linux so)
5. Node.js 中的 Native Addon https://github.com/nodejs/node/blob/2cc7a91a5d855b4ff78f21f1bb8d4e55131d0615/lib/internal/modules/cjs/loader.js#L1172 .node 格式文件 Node.js 对 .node 格式文件的加载方式:
6. Node.js 中的 Native Addon https://github.com/nodejs/node/blob/2cc7a91a5d855b4ff78f21f1bb8d4e55131d0615/lib/internal/modules/cjs/loader.js#L1172 .node 格式文件 Node.js 对 .node 格式文件的加载方式: modules/cjs/loader.js • Node.js 默认将 Native Addon 当作 CommonJS 格式模块加载 • 使用 process.dlopen 函数加载内容 • 目前源码包含对 ESM 的 import assets 逻辑的兼容,但实际上 Node.js 目前的 最新版 (17.1.0) 只支持 json 类型的 import assert
7. Node.js 中的 Native Addon https://github.com/Brooooooklyn/blake-hash/releases/tag/v1.3.0 Native Addon 文件内容 在 Linux 上使用 hexyl 查看 blake.darwin-x64.node 文件内容
8. Node.js 中的 Native Addon https://en.wikipedia.org/wiki/Executable_and_Linkable_Format Native Addon 文件内容 在 Linux 上使用 hexyl 查看 blake.darwin-x64.node 文件内容
9. Node.js 中的 Native Addon https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#/media/File:ELF_Executable_and_Linkable_Format_diagram_by_Ange_Al bertini.png
10. Node.js 中的 Native Addon https://github.com/nodejs/node/blob/v10.23.0/src/node.cc#L1232 Native Addon 文件内容 1. process.dlopen 2. 初始化 env 与 module.exports 对象,并传入 napi_register_module_v1 函数 3. 拿到返回的 module.exports 对象,后续将 native addon 当作普通的 CommonJS 模块处理
11. Rust 在前端/Node.js
12. Rust 在前端/Node.js https://leerob.io/blog/rust • Rust 在前端/Node.js 领域应用的历史 • 流行的 Rust 工具与库 • 作为工具与包的作者,如何发布 Rust 编写的前端/Node.js 工具链
13. Node.js 中的 Native Addon https://github.com/denoland/deno/issues/208#issuecomment-395739171 Rust 在前端/Node.js 领域应用的历史 Parcel 开始 从 C++ 切换 到 Rust,性 能提升 10 倍 以上 https://parce ljs.org/blog/b eta3/ SWC 切换到 N-API, 使用 optionalDep endencies 的 形式分发预 编译产物 Deno 宣布使 用 Rust Deno 开始使 用 SWC NAPI-RS 1.0 发布 Vercel 从 next.js 12 开 始正式引入 Rust 提升开 发体验 Rome 宣布 用 Rust 重写
14. Node.js 中的 Native Addon 目前流行的 Rust 编写的前端/Node.js 库 • Parcel 使用 SWC 与 NAPI-RS 实现 JavaScript/TypeScript 编译与优化逻辑,如 Tree-shaking 与 Scope hoist,minify 等。source map 模 块,png/jpeg 无损压缩,css 压缩与编译模块也是由 Rust + NAPI-RS 编写。 • Rome 是一个集 Linter/Compiler/Bundler/Formatter/Test Runner 为一体的工具链,目的是取代 Babel, ESLint, Webpack, Jest, Prettier 等 前端工具链。 最初由 TypeScript 编写,现已全面切换到 Rust。 • Next.js 是 Vercel 出品的前后端一体化 React 框架,目前它将所有定制的 Babel custom transformer 与默认的 TypeScript/JavaScript 编译 部分切换到了 Rust。 • Prisma 是下一代面向 TypeScript/JavaScript 的 ORM,Prisma 的数据库连接与查询引擎使用 Rust 与 NAPI-RS 实现。 • Ali ICE,Shopify FE 等团队使用 SWC 替换自定义 Babel plugin。
15. Node.js 中的 Native Addon 如何发布 Rust 编写的前端/Node.js 工具链 流行的 Native Addon 发布方式 • 只发布源码,通过 postinstall 脚本执行编译脚本在安装的时候编译成二进制。 • 在 CI 阶段预编译不同平台的二进制文件,在 postinstall 的时候下载下来。 • 在 CI 阶段预编译不同平台的二进制文件,将不同平台的二进制文件单独发布成包,然后通过 package.json 中的 cpu 与 os 字段限制这些 包可安装的平台。最终将所有平台的独立包指定为最终发布包的 optionalDependencies,主流的包管理工具比如 npm/pnpm/yarn 就可以 通过预设的 cpu 与 os 字段只下载对应平台的二进制包。
16. Node.js 中的 Native Addon 只发布源码 优点 • 对于库开发者来说,无需任何额外的工具链维护成本,只需要改动源码就可以轻松发布。 • 在用户的机器上用用户的工具链编译,兼容性是最好的。只要编译通过,基本上就可以运行,不会有系统组件比如 GLIBC libstdc++ 等不 兼容的情况。 缺点 • 大型项目的编译时间过长,并且占用大量 CPU 资源。 • 可能需要用户安装编译相关的工具链,比如 GCC CMAKE LLVM NASM Rust 等,前端开发者一般缺乏相关经验,对于很多项目很难设置 正确。 • 编译期间可能产生大量中间文件,不正确清理极其占用硬盘空间。 • 让很多 CI 的缓存逻辑无法工作,不得不重新全量下载 & 编译。 • postinstall 脚本不安全,未来可能会被默认禁用 https://github.com/npm/rfcs/pull/488
17. Node.js 中的 Native Addon 发布预编译产物,postinstall 的时候下载 优点 • 对于用户来说,减少了编译成本。 • 不需要考虑编译时工具链等问题。 • 没有中间编译产物,节省磁盘空间。 缺点 • postinstall 安装产物不受 lockfile 控制,会破坏很多 CI 的缓存逻辑,降低缓存复用率。 • 存放预编译产物的 CDN 无法照顾到全球的用户,比如大量项目使用 GitHub Releases 作为预编译二进制存放的服务,在国内访问困难。 • 因为要安装时下载,需要引入运行时不需要的依赖来完成下载,比如 node-pre-gyp 等。 • postinstall 脚本不安全,未来可能会被默认禁用 https://github.com/npm/rfcs/pull/488
18. Node.js 中的 Native Addon https://cdn.jsdelivr.net/npm/@napi-rs/blake-hash/package.json optionalDependencies
19. Node.js 中的 Native Addon optionalDependencies 优点 • 无感知,与安装普通 JavaScript 编写的 npm 包体验上没有区别。 • lockfile 与 cache 友好。 • 没有 postinstall 脚本。 缺点 • CI 配置复杂,发布流程繁琐。 • CI 环境链接的系统组件与用户使用环境可能存在不兼容情况,比如 GLIBC 版本。https://github.com/swc-project/swc/issues/1410
20. Node.js 中的 Native Addon 使用 Rust 开发 npm 包,如何选择发布模式 optionalDependencies • Rust 工具链体积巨大,编译缓慢,中间产物庞大,不适合在用户侧编译。 • postinstall 存在潜在的安全风险,前途未卜。 • 在 postinstall 下载预编译产物对 lockfile 与构建缓存非常不友好。 • 大量 Node 应用在企业私有网络内构建,无法访问外部 CDN,postinstall 下载失败率非常高。 • CI 的复杂配置与相关工具链的维护成本被 NAPI-RS cover 了,作为包作者无需关心。 主流包含 Native 工具链的包比如 esbuild SWC 和 next.js 都采用了 optioanlDependencies 的形式发布。https://github.com/evanw/esbuild/pull/1621
21. NAPI-RS
22. NAPI-RS https://napi.rs A framework for building compiled Node.js add-ons in Rust via Node-API • 介绍 • 使用场景 • 与 neon/node-bindgen 对比
23. NAPI-RS https://napi.rs 介绍 • 简单易用的 API • TypeScript .d.ts 文件生成 • 深度集成 Rust Tokio 异步运行时,轻松复用 Rust 异步代码 • 开箱即用的 cli,开发者无需为跨平台编译、自动化 CI 烦恼
24. NAPI-RS https://github.com/napi-rs/node-rs/tree/main/packages/crc32 简单易用的 API
25. NAPI-RS https://github.com/napi-rs/napi-rs/blob/main/examples/napi/index.d.ts TypeScript 类型生成 • 支持 Rust 的 struct + impl 生成 TypeScript Class 对应文件 • 联合类型与可选类型 • 异步类型生成,Rust 的 async fn 到 TypeScript 的 () => Promise<T> • Rust 代码注释透传到 .d.ts 文件 • 为无法自动精确生成的 Rust 函数重写 TypeScript 类型
26. NAPI-RS https://github.com/napi-rs/napi-rs/blob/main/examples/napi/index.d.ts TypeScript 类型自动生成
27. NAPI-RS https://github.com/napi-rs/napi-rs/blob/main/examples/napi/index.d.ts TypeScript 类型自动生成
28. NAPI-RS https://github.com/napi-rs/napi-rs/blob/main/examples/napi/index.d.ts TypeScript 类型自动生成
29. NAPI-RS https://github.com/napi-rs/napi-rs/blob/main/examples/napi/index.d.ts TypeScript 类型自动生成
30. NAPI-RS https://deno.com/blog/v1.9#native-http2-web-server Tokio 深度集成
31. NAPI-RS https://github.com/Brooooooklyn/hns Tokio 深度集成 • 复用 Tokio 生态,比如可以像 deno 那样将 http server 层用 Rust 实现而不是用 JavaScript 实现 • 可以受益于 Tokio 强大的工具链,比如 tokio-console 与 tokio-tracing • 提前享受 io_uring 等 libuv 未封装的内核新特性
32. NAPI-RS https://deno.com/blog/v1.9#native-http2-web-server Tokio 深度集成
33. NAPI-RS https://github.com/tokio-rs/console Tokio 深度集成
34. NAPI-RS https://github.com/tokio-rs/console Tokio 深度集成
35. NAPI-RS https://napi.rs/introduction/getting-started 开箱即用的 CLI • 新建项目与 GitHub Actions 模板,囊括跨平台编译与发布。 • 便捷的 build 命令,预置各种与 Node.js Addons 相关的编译参数,使用便捷。 • optionalDependencies 版本号管理,发布命令,GitHub CI Artifacts 命名与移动等功能全部封装在内,开发者无 需关系底层繁琐的实现细节与维护。
36. NAPI-RS https://napi.rs/introduction/getting-started 开箱即用的 CLI
37. NAPI-RS https://napi.rs/introduction/getting-started 开箱即用的 CLI
38. NAPI-RS https://napi.rs 使用场景 • 性能敏感的计算场景,输入输出简单。 • 需要使用 Node.js 不支持的 Native 特性,比如特定的 CPU 指令集,调用 GPU、USB 等,调用系统 API 等。 • 在 Server 端共享跨语言与框架的代码,比如 Prisma 使用 Rust 开发核心的 query engine,再封装到 Node.js Go 等。 • 利用 Rust 现代化的工具链 (包管理、跨平台编译等) 粘合现有的 C++ 代码,比如 https://github.com/Brooooooklyn/canvas
39. NAPI-RS https://github.com/Brooooooklyn/rust-to-nodejs-overhead-benchmark 使用场景 调用开销
40. NAPI-RS https://github.com/napi-rs/node-rs/tree/main/packages/crc32 使用场景 @node-rs/crc32
41. NAPI-RS 使用场景 各种类型 Hash 计算
42. NAPI-RS https://github.com/microsoft/node-pty/issues/507 使用场景 node-pty • 封装系统调用 • 跨平台 • 预编译
43. NAPI-RS https://github.com/Brooooooklyn/canvas 使用场景 @napi-rs/canvas • 将 C++ 编写的 Skia 项目编译成 static link library,通过 Rust FFI 机制引入使用。 • 组合现有的 Rust crates 比如 cssparser, base64, avif 等。 • 用 NAPI-RS 封装成 npm 包,对比 node-canvas 0 系统依赖,支持更多系统与 CPU 架构,功能更多。
44. NAPI-RS https://github.com/Brooooooklyn/canvas 使用场景 @napi-rs/canvas
45. NAPI-RS https://napi.rs/neon 与 Neon/node-bindgen 对比 • NAPI-RS 有更好的性能/更小的调用开销。 • NAPI-RS 提供从开发到发布的全流程解决方案,而不止是仅仅在 Rust 层提供 Node-API。 • NAPI-RS 对比 Neon 提供的 Node-API 更全面,包含了所有 Node-API 的 C API 封装。 • NAPI-RS 对比 node-bindgen 提供可选的 napi 版本选择,node-bindgen 只能支持最高版本的 Node.js,而 NAPI- RS 可以通过 features 来选择支持的 Node.js 版本,最低支持到 10.0.0。 • NAPI-RS 与 node-bindgen 的异步运行时不同,NAPI-RS 支持的是 Tokio,而 node-bindgen 支持的是 fluvio
46. Rust 与 Node 的未来展望
47. Rust 与 Node 的未来展望 • Rust 是 JavaScript 基建的未来,Next.js Parcel Prisma Rome 仅仅是革命的开始。 • 随着生态的丰富,未来用 Rust 来编写 Native Addon 会更加便捷,各种配套的基础建设会逐步丰富。 • 除了通过 Node-API ,未来 Rust 会有更多形式为 JavaScript 生态提供服务。比如 Rome 打算嵌入 JavaScript 引擎 并提供简单的 JavaScript API ,而不是依托于 Node-API 。比如 Deno 正在实现 –compact 模式 https://github.com/denoland/deno/issues/12577 。届时通过 Deno + Deno FFI + npm 包也能启动存量的 Node.js 项目。(NAPI-RS 已经计划通过 feature flag 支持无缝编译到 Deno FFI)。NAPI-RS 还计划提供无缝编译到 wasm,让包的发布和维护更简单。 • 越来越多的前端与 Node.js 业务相关的代码会出现在 Rust 的 crates 中,各种自研渲染引擎比如小程序和类 Flutter 方案更容易使用 Rust 开发。以后可能会诞生基于 Rust 生态的集运行时、构建、开发调试工具等周边的 一体化解决方案 。
48. Thanks

首页 - Wiki
Copyright © 2011-2024 iteam. Current version is 2.137.3. UTC+08:00, 2024-11-25 10:11
浙ICP备14020137号-1 $访客地图$