Deno应用集成WebAssembly(一)初探

时间:11/14/2024 19:17:55   作者:ChenReal    阅读:23

最近,看了一篇文章,介绍Node.js可以借助WebAssembly大幅度提高性能。好奇心起,于是便想玩一下。

众所周知,Node.js用来开发轻量应用十分方便,但在高并发或者计算较密集(例如:加密/解密、图形运算)的场景下性能比不上C、Rust、Go这些语言都弱很多。如果你的选择时“既要方便又要性能强悍”,那么完全可以来做一个hybird应用。用Node.js做应用的容器,把Rust等开发编译的wasm装载进来运行,似乎问题就解决。

WebAssembly是什么?

WebAssembly (wasm) 是一种具有广泛规范的简单机器模型和可执行格式。它被设计为可移植、紧凑并以本机速度或接近本机速度执行。包括 Rust、C、Java、Python、Ruby、.NET 在内等诸多语言都可通过 wasm 执行。此外,wasm 也可运行在各种硬件和操作系统之上,在浏览器运行也没有问题。

简单理解:就是一个可以跨平台跨语言调用的dll库。

Deno又是什么?

Deno跟Node.js本质是一样的,都是JavaScript 和 TypeScript 运行时环境,基于 V8 引擎并采用 Rust 编程语言构建。

说白了就是Node.js的替代产品,我用它的主要原因有三点:

  • 1、都是Rust语言构建的,跟Rust编译出来的wasm的“血缘”更接近
  • 2、Deno天然支持TypeScript,不需要像Node.js还要tsc编译成js才能运行
  • 3、Deno的项目结构和包管理方式简单清爽,写起来令人有一种心旷神怡的感觉

一、安装Deno

Deno安装很简单,几乎就是一行脚本就能搞定。

## 下载并安装deno
curl -fsSL https://deno.land/install.sh | sh
## 查看deno版本
deno -v

二、安装Rust

Rust安装脚本:

## 下载安装Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
## 查看cargo版本
cargo -V

三、使用wasmbuild模板编译wasm

为了简化开环境配置,我这里使用了工具wasmbuild。主要用它来创建项目模板,将Rust代码编译除了生成wasm二进制文件,还有配套的js胶水代码。

wasmbuild工具的源代码和详细使用说明可以参考:

https://github.com/denoland/wasmbuild

  • 首先创建,创建一个文件夹rust_wasm
    mkdir rust_wasm
    cd rust_wasm
    
  • 然后在这个文件夹创建deno.json的配置文件,并写入一下内容
    {
    "tasks": {
      "wasmbuild": "deno run -A jsr:@deno/wasmbuild@0.17.2"
    }
    }
    
  • 接下来,使用wmsbuild的模板新建项目
    deno task wasmbuild new
    
    • 很快就,出现一个名为rs_lib目录,里面就是用于生成wasm的Rust源代码。打开rs_lib/src/lib.rs我们可以看到已经有一些初始代码。
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn add(a: i32, b: i32) -> i32 {
  a + b
}
#[wasm_bindgen]
pub struct Greeter {
  name: String,
}
#[wasm_bindgen]
pub struct Greeter {
  name: String,
}
#[wasm_bindgen]
impl Greeter {
  #[wasm_bindgen(constructor)]
  pub fn new(name: String) -> Self {
    Self { name }
  }

  pub fn greet(&self) -> String {
    format!("Hello {}!", self.name)
  }
}
#[cfg(test)]
mod tests {
  use super::*;

  #[test]
  fn it_adds() {
    let result = add(1, 2);
    assert_eq!(result, 3);
  }

  #[test]
  fn it_greets() {
    let greeter = Greeter::new("world".into());
    assert_eq!(greeter.greet(), "Hello world!");
  }
}

这段代码非常简单直观,即便像我这种不太熟悉Rust代码的人也能秒懂。值得注意的是,声明函数或者对象必须加上#[wasm_bindgen]的属性标记,才能导出给JavaScript / TypeScript调用。其他的就过多解释了,我们继续往下走吧。

  • Rust代码有了,接着就可以开始编译wasm了
    deno task wasmbuild
    

编译成完成后,生成一个lib文件夹,里面便是我们期盼很久的wasm文件了!

如果编译出现报错:linker 'cc' not found,先把gcc安装好,然后再试。

apt-get install gcc

执行js脚本

项目目录里,还有一个叫mod.js的代码没有介绍到。这是deno执行脚本入口,现在是时候打开一探了。

import { instantiate } from "./lib/rs_lib.generated.js";

const { add, Greeter } = await instantiate();

// adds
console.log(add(1, 1));

// greets
const greeter = new Greeter("world");
console.log(greeter.greet());

里面的代码很少,也很简单。它主要做两件事情:

  • 引入刚刚编译rust代码生成wasm的胶水文件./lib/rs_lib.generated.js
  • 调用wasm里的函数add和类Greeter的方法

我们马上执行命令看看效果。

deno run --allow-read mod.js

运行结果:

image.png

总结

为了弥补Node.js应用的先天性能不足,本文提供了一种新的思路:Rust 编译成 WebAssembly 能够提供接近原生的性能,给Node.js装载调用。计算密集型任务中,这通常比 JavaScript 执行得更快。

虽然,Rust 和 WebAssembly 在性能和安全性方面提供了显著的优势,但在易用性、工具集成和社区支持方面可能存在一些挑战。对于需要高性能计算的应用程序,或者那些对安全性有严格要求的项目,使用Rust和 WebAssembly 可能是一个很好的选择。然而,对于需要快速开发和广泛社区支持的项目,纯JavaScript 解决方案可能更好。

总之,大家可以根据自己的项目实际需要来选择更加合适的方案。

 

评论
0/200