从 WasmEdge 运行环境读写 Rust Wasm 应用的时序数据

WebAssembly (Wasm) 正在成为一个广受欢迎的编译目标,帮助开发者构建可迁移平台的应用。最近 Greptime 和 WasmEdge 协作,支持了在 WasmEdge 平台上的 Wasm 应用通过 MySQL 协议读写 GreptimeDB 中的时序数据。

什么是 WebAssembly

WebAssembly 是一种新的指令格式,同时具备了跨平台和接近原生机器代码的执行速度。** 通过将 C/C++ 或 Rust 代码编译成 WebAssembly ,可以在浏览器中提升程序的性能。而在浏览器外的其他运行环境,尤其是 CDN 或 IoT 的边缘端,我们也可以利用 WebAssembly 实现沙盒、动态加载的插件机制等高级的功能。

什么是 WasmEdge

WasmEdge 是 CNCF 的沙箱项目,提供上文提到的沙盒能力,允许开发者在 WebAssembly 标准的基础上,进一步扩展其能访问的资源和接口。例如,WasmEdge 为 Wasm 提供了额外的 TLS、网络能力和 AI 能力,大大丰富了使用场景。

WasmEdge GitHub 地址:
https://github.com/WasmEdge/WasmEdge

安装 GreptimeDB 和 WasmEdge

如果你已经安装了 GreptimeDB ,可以跳过这个步骤。

下载 GreptimeDB 并运行:

curl -L https://github.com/GreptimeTeam/greptimedb/raw/develop/scripts/install.sh | sh
./greptime standalone start


安装 WasmEdge:

curl -sSf https://raw.githubusercontent.com/WasmEdge/WasmEdge/master/utils/install.sh | bash -s

编写 GreptimeDB 的 WASM 应用

在 WasmEdge 中,我们可以使用 MySQL 协议,让 Rust 语言编写的应用程序连接到 GreptimeDB。

首先通过 `cargo new` 创建一个新的 Rust 项目,我们的编译目标将是 `wasm32-wasi`,可以在项目根目录下创建 `.cargo/config.toml` 文件,指定默认编译目标,之后就无需在每次 `cargo build` 命令后专门指定 `--target` 了。

# .cargo/config.toml
[build]
target = "wasm32-wasi"

编辑 `Cargo.toml` 增加依赖。`mysql_async` 的应用需要 `tokio` 运行时,WasmEdge 维护了这两个库的修改版本,使他们能够编译成 WebAssembly 代码,并且运行到 WasmEdge 环境中。

[package]
name = "greptimedb"
version = "0.1.0"
edition = "2021"

[dependencies]
mysql_async_wasi = "0.31"
time = "0.3"
tokio_wasi = { version = "1", features = [ "io-util", "fs", "net", "time", "rt", "macros"] }

进一步编辑 `src/main.rs` 文件,加入数据库访问的逻辑。这段代码将演示:

1. 通过环境变量读取数据库地址,并创建连接池;
2. 执行 SQL 语句创建数据表;
3. 插入数据;
4. 查询数据。

定义数据结构:

#[derive(Debug)]
struct CpuMetric {
    hostname: String,
    environment: String,
    usage_user: f64,
    usage_system: f64,
    usage_idle: f64,
    ts: i64,
}

impl CpuMetric {
    fn new(
        hostname: String,
        environment: String,
        usage_user: f64,
        usage_system: f64,
        usage_idle: f64,
        ts: i64,
    ) -> Self {
        Self {
            hostname,
            environment,
            usage_user,
            usage_system,
            usage_idle,
            ts,
        }
    }
}

初始化数据库连接池:

use mysql_async::{
    prelude::*, Opts, OptsBuilder, Pool, PoolConstraints, PoolOpts, Result,
};
use time::PrimitiveDateTime;

fn get_url() -> String {
    if let Ok(url) = std::env::var("DATABASE_URL") {
        let opts = Opts::from_url(&url).expect("DATABASE_URL invalid");
        if opts
            .db_name()
            .expect("a database name is required")
            .is_empty()
        {
            panic!("database name is empty");
        }
        url
    } else {
        "mysql://root:pass@127.0.0.1:3306/mysql".into()
    }
}


#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<()> {
    // Alternative: The "easy" way with a default connection pool
    // let pool = Pool::new(Opts::from_url(&*get_url()).unwrap());
    // let mut conn = pool.get_conn().await.unwrap();

    // Below we create a customized connection pool
    let opts = Opts::from_url(&*get_url()).unwrap();
    let builder = OptsBuilder::from_opts(opts);
    // The connection pool will have a min of 1 and max of 2 connections.
    let constraints = PoolConstraints::new(1, 2).unwrap();
    let pool_opts = PoolOpts::default().with_constraints(constraints);

    let pool = Pool::new(builder.pool_opts(pool_opts));
    let mut conn = pool.get_conn().await.unwrap();
    
    
    
    Ok(())
}

创建数据表:

    // Create table if not exists
    r"CREATE TABLE IF NOT EXISTS wasmedge_example_cpu_metrics (
    hostname STRING,
    environment STRING,
    usage_user DOUBLE,
    usage_system DOUBLE,
    usage_idle DOUBLE,
    ts TIMESTAMP,
    TIME INDEX(ts),
    PRIMARY KEY(hostname, environment)
);"
    .ignore(&mut conn)
    .await?;
 

插入数据:

    let metrics = vec![
        CpuMetric::new(
            "host0".into(),
            "test".into(),
            32f64,
            3f64,
            4f64,
            1680307200050,
        ),
        CpuMetric::new(
            "host1".into(),
            "test".into(),
            29f64,
            32f64,
            50f64,
            1680307200050,
        ),
        CpuMetric::new(
            "host0".into(),
            "test".into(),
            32f64,
            3f64,
            4f64,
            1680307260050,
        ),
        CpuMetric::new(
            "host1".into(),
            "test".into(),
            29f64,
            32f64,
            50f64,
            1680307260050,
        ),
        CpuMetric::new(
            "host0".into(),
            "test".into(),
            32f64,
            3f64,
            4f64,
            1680307320050,
        ),
        CpuMetric::new(
            "host1".into(),
            "test".into(),
            29f64,
            32f64,
            50f64,
            1680307320050,
        ),
    ];

    r"INSERT INTO wasmedge_example_cpu_metrics (hostname, environment, usage_user, usage_system, usage_idle, ts)
      VALUES (:hostname, :environment, :usage_user, :usage_system, :usage_idle, :ts)"
        .with(metrics.iter().map(|metric| {
            params! {
                "hostname" => &metric.hostname,
                "environment" => &metric.environment,
                "usage_user" => metric.usage_user,
                "usage_system" => metric.usage_system,
                "usage_idle" => metric.usage_idle,
                "ts" => metric.ts,
            }
        }))
        .batch(&mut conn)
        .await?;

查询数据:

    let loaded_metrics = "SELECT * FROM wasmedge_example_cpu_metrics"
        .with(())
        .map(
            &mut conn,
            |(hostname, environment, usage_user, usage_system, usage_idle, raw_ts): (
                String,
                String,
                f64,
                f64,
                f64,
                PrimitiveDateTime,
            )| {
                let ts = raw_ts.assume_utc().unix_timestamp() * 1000;
                CpuMetric::new(
                    hostname,
                    environment,
                    usage_user,
                    usage_system,
                    usage_idle,
                    ts,
                )
            },
        )
        .await?;
    println!("{:?}", loaded_metrics);

WasmEdge 团队提供的 `tokio` 和 `mysql_async` 库与原始版本编程接口完全一致,因此可以无缝地将普通 Rust 应用切换到 WebAssembly 平台上。

编译这个项目,我们可以获得 greptimedb.wasm 文件:

cargo build
ls -lh target/wasm32-wasi/debug/greptimedb.wasm

通过 WasmEdge 运行我们的程序:

wasmedge --env "DATABASE_URL=mysql://localhost:4002/public" target/wasm32-wasi/debug/greptimedb.wasm

上面这段示例程序已经纳入了 WasmEdge 的数据库使用示例,你可以在 GitHub 仓库找到完整的代码:
https://github.com/WasmEdge/wasmedge-db-examples/tree/main/greptimedb。

总结

WasmEdge 为 WebAssembly 应用提供了更多的扩展能力。如果你也将应用部署在 WebAssembly 环境里,未来我们还可以使用 OpenTelemetry SDK 采集指标数据直接存储到 GreptimeDB 。现在就下载 GreptimeDB 或开通 GreptimeCloud 实例运行上面的例子吧。

  • 37
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: pixijs是一款基于JavaScript的2D游戏和应用程序开发框架,它提供了强大的图形渲染能力和丰富的功能。它被广泛应用于 Web 和移动平台上,可用于开发各种类型的游戏、交互式应用和可视化效果。 Rust是一种系统级编程语言,它被设计用于开发快速、安全和可靠的软件。它强调安全性、并发性和性能,为开发人员提供了可靠的内存管理机制和丰富的工具集。Rust可以用于开发各种类型的应用,包括系统级程序、Web 后端、嵌入式设备和游戏等。 WASM(WebAssembly)是一种低级字节码格式,被设计用于在 Web 浏览器中运行高性能的跨平台应用程序。它可以将其他语言编译为可在现代浏览器中运行的机器码,提供了比 JavaScript 更高的性能和更接近本地的执行速度。WASM 可以用于各种应用场景,包括游戏、图形渲染、音视频处理和科学计算等。 结合pixijs、Rust和WASM,我们可以开发出高性能的图形应用程序。通过使用pixijs进行游戏和图形渲染的开发,结合Rust的编程语言特性,可以编写出更加安全和高效的代码。然后,使用WASMRust代码编译为WebAssembly模块,使其能够在浏览器中运行。这样,我们可以在浏览器中实现更加复杂和流畅的游戏、交互性应用和图形效果,获得更好的用户体验。同时,使用Rust和WASM也可以提供更好的性能和安全性,保护用户的数据和隐私。总之,结合pixijs、Rust和WASM可以创建出令人惊叹的Web应用程序。 ### 回答2: pixijs是一个轻量级的2D渲染引擎,具有强大的性能和跨平台的能力。它使用JavaScript编写,可用于创建交互式的Web应用程序和游戏。pixijs提供了许多功能丰富的API,例如图形渲染、粒子效果、动画、交互、音频等,使开发者能够轻松地构建出色的视觉效果和丰富的用户体验。 Rust是一种系统级的、多范式的编程语言,注重安全、并发和性能。与传统的编程语言不同,Rust在编译时就能够检测到多种内存安全错误,如空指针引用和数据竞争等,从而提高代码的稳定性和可靠性。Rust还支持并发编程,通过轻量级的线程和通道,开发者可以编写可靠、高性能的并行代码。此外,Rust还具有与C和C++兼容的特性,可以方便地集成现有的代码。 WASM是WebAssembly的缩写,它是一种面向Web的二进制代码格式。WASM能够以高效的方式在现代Web浏览器中运行,并且可以与JavaScript无缝集成。WASM提供了一种性能优于JavaScript的解决方案,特别适用于需要处理复杂计算或需要更高性能的应用程序。WASM还具有跨平台的能力,能够在不同的客户端设备上运行,包括桌面电脑、移动设备等。 在开发Web应用程序和游戏时,可以结合使用pixijs、Rust和WASM来实现更高效、更强大的功能。借助pixijs的强大渲染引擎,可以创建出色的图形效果和动画;Rust的安全性和并发能力可以提高应用程序的可靠性和性能;而WASM的高性能和跨平台特性则有助于在不同的客户端设备上实现高效的运行。综上所述,pixijs、Rust和WASM的结合可以为开发者带来更好的开发体验和用户体验。 ### 回答3: PixiJS是一个轻量级的JavaScript渲染引擎,用于创建各种类型的交互性和高性能的2D游戏、动画和应用程序。它具有简单易用的API和高度可扩展的功能,可以轻松地将图形和动画效果应用到Web应用程序中。PixiJS还支持WebGL和Canvas渲染,因此可以在不同的平台上运行,并提供了优化性能的工具和功能。 Rust是一种系统级编程语言,强调安全、并发和性能。它的设计目标是提供像C和C++一样的低级控制和高性能,但又避免了这些语言中常见的内存安全问题。Rust通过引入所有权和借用的概念,确保在编译时检查程序的内存安全性,并减少了常见的内存错误,例如数据竞争和空指针。 WebAssembly(简称Wasm)是一种用于在Web浏览器中运行底层代码的二进制指令格式。它是一种可移植、高性能且安全的技术,可以在Web浏览器中运行各种语言编写的程序,如Rust、C++和C#。Wasm在实现跨平台一致性和性能方面具有优势,并允许开发者利用其他语言的特性和库来开发Web应用程序和游戏。 PixiJS、Rust和Wasm之间可以有一些结合和互补的应用。例如,开发者可以使用Rust编写高性能的游戏逻辑和算法,并将其编译为Wasm模块,然后使用PixiJS进行图形渲染和交互性,从而创建出强大且高效的Web游戏。由于Rust的内存安全保证,开发者可以更轻松地编写复杂的游戏逻辑,而Wasm和PixiJS提供了更好的性能和可视化效果。总之,这三个技术结合可以帮助开发者创建出更具吸引力和优化的Web体验。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值