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-Lhttps://github.com/GreptimeTeam/greptimedb/raw/develop/scripts/install.sh|sh ./greptimestandalonestart
安装 WasmEdge:
curl-sSfhttps://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文件,加入数据库访问的逻辑。这段代码将演示:
通过环境变量读取数据库地址,并创建连接池;
执行 SQL 语句创建数据表;
插入数据;
查询数据。
定义数据结构:
#[derive(Debug)] structCpuMetric{ hostname:String, environment:String, usage_user:f64, usage_system:f64, usage_idle:f64, ts:i64, } implCpuMetric{ fnnew( 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, } }
}
初始化数据库连接池:
usemysql_async::{
prelude::*,Opts,OptsBuilder,Pool,PoolConstraints,PoolOpts,Result,
};
usetime::PrimitiveDateTime;
fnget_url()->String{
ifletOk(url)=std::var("DATABASE_URL"){
letopts=Opts::from_url(&url).expect("DATABASE_URLinvalid");
ifopts
.db_name()
.expect("adatabasenameisrequired")
.is_empty()
{
panic!("databasenameisempty");
}
url
}else{
"mysql://root:pass@127.0.0.1:3306/mysql".into()
}
}
#[tokio::main(flavor="current_thread")]
asyncfnmain()->Result<()>{
//Alternative:The"easy"waywithadefaultconnectionpool
//letpool=Pool::from_url(&*get_url()).unwrap());
//letmutconn=pool.get_conn().await.unwrap();
//Belowwecreateacustomizedconnectionpool
letopts=Opts::from_url(&*get_url()).unwrap();
letbuilder=OptsBuilder::from_opts(opts);
//Theconnectionpoolwillhaveaminof1andmaxof2connections.
letconstraints=PoolConstraints::new(1,2).unwrap();
letpool_opts=PoolOpts::default().with_constraints(constraints);
letpool=Pool::new(builder.pool_opts(pool_opts));
letmutconn=pool.get_conn().await.unwrap();
Ok(())
}
创建数据表:
//Createtableifnotexists r"CREATETABLEIFNOTEXISTSwasmedge_example_cpu_metrics( hostnameSTRING, environmentSTRING, usage_userDOUBLE, usage_systemDOUBLE, usage_idleDOUBLE, tsTIMESTAMP, TIMEINDEX(ts), PRIMARYKEY(hostname,environment) );" .ignore(&mutconn) .await?;
插入数据:
letmetrics=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"INSERTINTOwasmedge_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(&mutconn)
.await?;
查询数据:
letloaded_metrics="SELECT*FROMwasmedge_example_cpu_metrics"
.with(())
.map(
&mutconn,
|(hostname,environment,usage_user,usage_system,usage_idle,raw_ts):(
String,
String,
f64,
f64,
f64,
PrimitiveDateTime,
)|{
letts=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 文件:
cargobuild ls-lhtarget/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 实例运行上面的例子吧。
审核编辑:汤梓红
-
时序
+关注
关注
5文章
402浏览量
38626 -
MySQL
+关注
关注
1文章
897浏览量
29230 -
编译
+关注
关注
0文章
688浏览量
34944 -
GitHub
+关注
关注
3文章
484浏览量
18424 -
Rust
+关注
关注
1文章
240浏览量
7477
原文标题:从 WasmEdge 运行环境读写 Rust Wasm 应用的时序数据
文章出处:【微信号:Rust语言中文社区,微信公众号:Rust语言中文社区】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
如何在Rust中读写文件
多片段时序数据建模预测实践资料分享
关于时序数据库的内容
Wasm软件生态系统安全分析
TableStore时序数据存储 - 架构篇
时序数据库的前世今生
华为时序数据库为智慧健康养老行业贡献应用之道
华为PB级时序数据库Gauss DB,助力海量数据处理
WasmEdge增加了Tokio支持
物联网场景海量时序数据存储与处理的关键技术
涂鸦推出NekoDB时序数据库,助力全球客户实现低成本部署
什么是wasm组件?使用Rust开发wasm组件实战

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