Rust 微服务在服务器端 WebAssembly 中






3.80/5 (6投票s)
构建和部署 Rust 微服务到 WASM 运行时所需的工具和技术的概述
Rust 编程语言在过去几年中获得了主流应用。它始终被开发者评为最受欢迎的编程语言,并且已被 Linux 内核接纳。Rust 使开发人员能够编写正确且内存安全的程序,其速度和体积与 C 程序一样小。它非常适合需要高可靠性和高性能的基础设施软件,包括服务器端应用程序。
然而,对于服务器端应用程序,Rust 也带来了一些挑战。Rust 程序被编译成本机机器码,这不具可移植性,在多租户云环境中也不安全。我们还缺乏用于在云中管理和编排本地应用程序的工具。
因此,服务器端 Rust 应用程序通常在虚拟机或 Linux 容器中运行,这带来了显著的内存和 CPU 开销。这削弱了 Rust 在效率方面的优势,并使其难以在资源受限的环境(如边缘数据中心和边缘云)中部署服务。这个问题的解决方案是WebAssembly (Wasm)。
Wasm 程序最初是作为 Web 浏览器中的安全运行时,现在可以在其自己的沙箱中安全地隔离。借助新一代 Wasm 运行时,例如云原生计算基金会的WasmEdge Runtime,您现在可以在服务器上运行 Wasm 应用程序。您可以将 Rust 程序编译为 Wasm 字节码,然后在云中部署 Wasm 应用程序。
根据发表在 IEEE Software 上的研究,与 Linux 容器中本机编译的 Rust 应用程序相比,Wasm 应用程序可以快 100 倍(尤其是在启动时),体积小 1/100。这使得它们特别适合资源受限的环境,如边缘云。
Wasm 运行时沙箱比 Linux 容器具有更小的攻击面,并提供更好的隔离性。此外,Wasm 运行时可跨操作系统和硬件平台移植。一旦 Rust 程序被编译成 Wasm,它就可以从开发到生产,从云到边缘的任何地方运行。
在本文中,我们将介绍构建 Rust 微服务所需的工具、库、API、框架和技术。我们还将演示如何在 WasmEdge WebAssembly
Runtime 中部署、运行和扩展这些微服务。
注意WasmEdge 是一个执行 WebAssembly 程序的运行时环境。WebAssembly 是一种低级、可移植的字节码,可以在各种环境中运行,包括 Web 浏览器、独立进程和云平台。
WasmEdge 直接在解释器模式下执行 WebAssembly 程序;在即时编译(AOT)模式下,WasmEdge 首先将 WASM 字节码编译为共享对象或通用 wasm 格式,然后 WasmEdge 执行它。
为了提供安全性和隔离性,WasmEdge 在沙箱环境中运行 WebAssembly 程序。这意味着 WebAssembly 程序在它明确允许访问的资源和它可以执行的操作方面受到限制,这有助于防止其影响系统的其余部分或访问敏感信息。
WasmEdge 被设计为轻量级且内存占用少,非常适合在云原生服务和嵌入式系统等资源受限的环境中使用。它还被设计为速度快,在执行 WebAssembly 程序时提供近乎本机的性能。
跳转到
必备组件
要阅读本文,您应该具备以下条件
- 微服务设计模式基础知识
- Linux 操作系统基础知识
- 熟悉 Rust 编程语言
- SQL 数据库基础知识
创建 Web 服务
微服务首先是一个 Web 服务器。WasmEdge
Runtime 支持异步和非阻塞网络套接字。您可以使用 Rust 编写网络应用程序,将其编译为 Wasm,然后在 WasmEdge
Runtime 中运行。在 Rust 生态系统中,WasmEdge
支持以下内容
下面的示例来自 microservice-rust-mysql 演示应用程序,它展示了如何在 hyper 中为 WasmEdge
创建 Web 服务器。Web 服务器的主要监听循环如下
let addr = SocketAddr::from(([0, 0, 0, 0], 8080));
let make_svc = make_service_fn(|_| {
let pool = pool.clone();
async move {
Ok::<_, Infallible>(service_fn(move |req| {
let pool = pool.clone();
handle_request(req, pool)
}))
}
});
let server = Server::bind(&addr).serve(make_svc);
if let Err(e) = server.await {
eprintln!("server error: {}", e);
}
Ok(())
一旦收到请求,事件处理程序 handle_request()
就会异步调用,以便它可以处理多个并发请求。它根据 request
方法和路径生成响应
async fn handle_request(req: Request<Body>,
pool: Pool) -> Result<Response<Body>, anyhow::Error> {
match (req.method(), req.uri().path()) {
(&Method::GET, "/") => Ok(Response::new(Body::from(
"... ...",
))),
// Simply echo the body back to the client.
(&Method::POST, "/echo") => Ok(Response::new(req.into_body())),
(&Method::GET, "/init") => {
let mut conn = pool.get_conn().await.unwrap();
"DROP TABLE IF EXISTS orders;".ignore(&mut conn).await?;
"CREATE TABLE orders (order_id INT, product_id INT, quantity INT,
amount FLOAT, shipping FLOAT, tax FLOAT,
shipping_address VARCHAR(20));".ignore(&mut conn).await?;
drop(conn);
Ok(Response::new(Body::from("{\"status\":true}")))
}
(&Method::POST, "/create_order") => {
// ... ...
}
// Return the 404 Not Found for other routes.
_ => {
let mut not_found = Response::default();
*not_found.status_mut() = StatusCode::NOT_FOUND;
Ok(not_found)
}
}
}
现在我们有了 Web 服务的 HTTP 服务器。
创建 Web 服务客户端
典型的 Web 服务还需要消耗其他 Web 服务。通过 tokio 和/或 mio crate,WasmEdge
应用程序可以轻松地集成用于 Web 服务的 HTTP 客户端。WasmEdge
支持以下 Rust crate
下面的示例展示了如何从微服务向 Web 服务 API 发起 HTTP POST
请求
let client = reqwest::Client::new();
let res = client
.post("http://eu.httpbin.org/post")
.body("msg=WasmEdge")
.send()
.await?;
let body = res.text().await?;
println!("POST: {}", body);
创建数据库客户端
大多数微服务都由数据库支持。WasmEdge
支持以下用于 MySQL 驱动程序的 Rust crate
myql
crate 是一个同步 MySQL 客户端mysql_async
是一个异步 MySQL 客户端
下面的示例展示了如何将一组记录插入数据库表
let orders = vec![
Order::new(1, 12, 2, 56.0, 15.0, 2.0, String::from("Mataderos 2312")),
Order::new(2, 15, 3, 256.0, 30.0, 16.0, String::from("1234 NW Bobcat")),
Order::new(3, 11, 5, 536.0, 50.0, 24.0, String::from("20 Havelock")),
Order::new(4, 8, 8, 126.0, 20.0, 12.0, String::from("224 Pandan Loop")),
Order::new(5, 24, 1, 46.0, 10.0, 2.0, String::from("No.10 Jalan Besar")),
];
r"INSERT INTO orders (order_id, production_id, quantity,
amount, shipping, tax, shipping_address)
VALUES (:order_id, :production_id, :quantity, :amount,
:shipping, :tax, :shipping_address)"
.with(orders.iter().map(|order| {
params! {
"order_id" => order.order_id,
"production_id" => order.production_id,
"quantity" => order.quantity,
"amount" => order.amount,
"shipping" => order.shipping,
"tax" => order.tax,
"shipping_address" => &order.shipping_address,
}
}))
.batch(&mut conn)
.await?;
下面的示例展示了如何查询数据库表并返回记录集合
let loaded_orders = "SELECT * FROM orders"
.with(())
.map(
&mut conn,
|(order_id, production_id, quantity, amount, shipping, tax, shipping_address)| {
Order::new(
order_id,
production_id,
quantity,
amount,
shipping,
tax,
shipping_address,
)
},
)
.await?;
dbg!(loaded_orders.len());
dbg!(loaded_orders);
构建、部署和运行微服务
microservice-rust-mysql
项目提供了数据库驱动的微服务的完整示例。让我们以它为例来构建、部署和运行此服务。
Docker CLI 和 Docker Desktop 为 WasmEdge 应用程序开发提供了无缝支持。从项目仓库的根目录开始,您只需一个命令即可构建并启动微服务的所有组件(即 WasmEdge
应用程序和 MariaDB 数据库服务器)
docker compose up
然后,您可以使用 curl
通过微服务测试对数据库的 CRUD 操作。
或者,您可以
以下命令在 Linux 系统上安装上述先决条件
// Install Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
// Install WasmEdge
curl -sSf https://raw.githubusercontent.com/WasmEdge/WasmEdge/master/utils/install.sh |
bash -s -- -e all
// Install MySQL. It is available as a package in most Linux distros
sudo apt-get update
sudo apt-get -y install mysql-server libmysqlclient-dev
sudo service mysql start
接下来,将微服务应用程序构建为 Wasm 字节码
cargo build --target wasm32-wasi --release
然后,在 WasmEdge
Runtime 中启动微服务
wasmedge --env "DATABASE_URL=mysql://user:passwd@127.0.0.1:3306/mysql"
order_demo_service.wasm
然后,您可以使用微服务的 Web API 来访问数据库
// Init the database table
curl https://:8080/init
// Insert a set of records
curl https://:8080/create_orders -X POST -d @orders.json
// Query the records from the database
curl https://:8080/orders
// Update the records
curl https://:8080/update_order -X POST -d @update_order.json
// Delete a record by its id
curl https://:8080/delete_order?id=2
生产部署
到目前为止,我们已经看到了一个完整的数据库驱动的微服务。然而,在现实世界中,一个公司可能拥有数百个微服务。它们必须由 Kubernetes 等云原生框架进行管理和编排。
WasmEdge 应用程序完全符合 OCI 标准。它们可以被管理并存储在 Docker Hub 或其他 Open Container Initiative 存储库中。通过crun 集成,WasmEdge 可以与同一个 Kubernetes 集群中的 Linux 容器应用程序并排运行。此存储库展示了如何在流行的容器工具链(包括 CRI-O、containerd、Kubernetes、Kind、OpenYurt、KubeEdge 等)中运行 WasmEdge 应用程序。
此外,微服务通常与服务框架一起部署。例如,Dapr 是一个流行的微服务运行时框架。它为每个微服务提供一个“sidecar”服务。微服务通过 Dapr API 访问 sidecar,以发现和调用网络上的其他服务、管理状态数据以及访问消息队列。
WASI(WebAssembly System Interface)的Dapr SDK 使基于 WasmEdge 的微服务能够访问其附加的 Dapr sidecar。还有一个包含 Jamstack 静态 Web 前端、三个微服务和一个数据库服务的完整演示应用程序。
结论
在本文中,我们讨论了为什么 WebAssembly
是基于 Rust 的服务器端应用程序的绝佳运行时沙箱格式。我们通过具体的代码示例,演示了如何创建 HTTP Web 服务、消耗其他 Web 服务、从 Rust 访问关系数据库,然后在 WasmEdge
中运行编译后的应用程序。我们还探讨了部署问题,例如 Kubernetes 和 Dapr 集成。
有了这些 crate 和模板应用程序,您就可以用 Rust 构建自己的轻量级微服务了!
除了微服务,WasmEdge
Runtime 还可以在许多不同的场景中广泛用作应用程序沙箱
flows.network
是一个用于 SaaS 自动化的无服务器平台。您可以使用 Rust 和WebAssembly
为 SaaS 产品创建机器人、自定义项和连接器。- 您可以创建用户自定义函数(UDF)和用于数据库的提取-转换-加载(ETL)函数,并将这些函数以“无服务器”方式嵌入或放置在数据库旁边。
- 您可以使用
WebAssembly
为边缘设备创建可移植的高性能应用程序,在 Android、Open Harmony,甚至 seL4 RTOS 上运行。 WebAssembly
被广泛用作区块链执行智能合约的运行时。例如,WasmEdge
为 Polkadot、FileCoin 和 XRP(Ripple)网络运行节点和智能合约。下一代以太坊区块链虚拟机,即 ewasm,也是基于WebAssembly
。
要了解更多关于 WasmEdge
的信息,请参阅官方文档。
历史
- 2022 年 12 月 23 日:初始版本