本文翻译自 https://blog.logrocket.com/rust-microservices-server-side-webassembly/。在 reddit 获超200赞。
过去几年中,Rust 编程语言已获得主流采用。它一直被开发者评为最受欢迎的编程语言[1],并已被Linux内核接受[2]。 Rust 使开发者能够编写正确且内存安全的程序,这些程序与 C 程序一样速度快,占用空间小。它非常适合需要高可靠性和高性能的基础架构软件,包括服务端应用程序[3]。
然而,对于服务端应用程序,Rust 也提出了一些挑战。Rust 程序被编译成原生机器代码,在多租户云环境中不可移植且不安全。我们还缺乏在云中管理和编排原生应用程序的工具。
因此,服务端 Rust 应用程序通常在 VM 或 Linux 容器中运行,这会带来大量的内存和 CPU 开销。这削弱了 Rust 在效率方面的优势,并且难以在资源受限的环境中部署服务,例如边缘数据中心和边缘云。这个问题的解决方案是 WebAssembly[4] (Wasm)。
Wasm 最初是作为 Web 浏览器中的安全运行时,Wasm 程序可以安全地隔离在自己的沙箱中。借助新一代的 Wasm 运行时,例如 Cloud Native Computing Foundation 的 WasmEdge Runtime[5],你现在可以在服务器上运行 Wasm 应用程序。可以将 Rust 程序编译为 Wasm 字节码,然后将 Wasm 应用程序部署到云端。
根据发表在 IEEE Software 上的研究[6],与 Linux 容器中原生编译的 Rust 应用相比,Wasm 应用可以快 100 倍(尤其是在启动时)且大小仅为 1/100。因而特别适合边缘云等资源受限的环境。
Wasm 运行时沙箱的攻击面要小得多,并且比 Linux 容器提供了更好的隔离。此外,Wasm 运行时可跨操作系统和硬件平台移植。一旦将 Rust 程序编译到 Wasm 中,就可以在从开发到生产、从云到边缘的任何地方运行。
WebAssembly 沙箱中 Rust 微服务的拆解
本文中,我们将介绍在 Rust 中构建微服务所需的工具、库、API、框架和技术。我们还将演示如何在 WasmEdge WebAssembly Runtime 中部署、运行和扩展这些微服务。
- 准备工作[7]
- 创建 Web 服务[8]
- 创建 Web 服务客户端[9]
- 创建数据库客户端[10]
- 构建、运行和部署微服务[11]
- 进入生产[12]
继续阅读本文,需具备以下条件:
- 微服务设计模式基础知识
- Linux操作系统的基本知识
- 熟悉 Rust 编程语言
- SQL 数据库基础知识
微服务首先是一个 Web 服务器。WasmEdge Runtime 支持异步和 non-blocking network socket。你可以用 Rust 编写 networking 应用程序,将它们编译成 Wasm,然后在 WasmEdge Runtime 中运行它们。在 Rust 生态系统中,WasmEdge 支持以下内容:
- tokio[13] 和 mio[14] crates, 创建异步 networking 应用程序
- hyper[15] crate,用于创建 HTTP 服务器和客户端应用程序
下面的示例来自 microservice-rust-mysql[16] demo app,展示了如何为 WasmEdge 在 hyper[17] 创建一个 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(), 将被异步调用,以便它可以处理多个并发请求。它根据请求方法和路径生成响应:
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 crates,WasmEdge 应用程序可以轻松地为 Web 服务合并 HTTP 客户端。WasmEdge 支持以下 Rust crate:
- reqwest[18] crate, 用于易于使用的 HTTP 客户端
- HTTP 和 HTTPS 客户端的 http_req[19] 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 crates:
- myql[20] 是一个同步 MySQL 客户端
- mysql_async[21] 是一个异步 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[22] 项目提供了一个数据库驱动的微服务的完整例子。让我们以它为例,来构建、部署和运行该服务。
docker CLI 和 Docker Desktop 为 WasmEdge 应用程序开发提供无缝支持。在项目 repo 的根目录中,你只需要一个命令来构建和启动微服务的所有组件(即 WasmEdge 应用程序和 mariaDB[23] 数据库服务器) :
docker compose up
然后,可以使用 curl 通过微服务测试数据库上的 CRUD 操作。或者,你可以:
- 安装 Rust 编译器[24]
- 安装 WasmEdge Runtime[25]
- 安装 MySQL 数据库服务器[26]
以下命令在 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 http://localhost:8080/init
// Insert a set of records
curl http://localhost:8080/create_orders -X POST -d @orders.json
// Query the records from the database
curl http://localhost:8080/orders
// Update the records
curl http://localhost:8080/update_order -X POST -d @update_order.json
// Delete a record by its id
curl http://localhost:8080/delete_order?id=2
进入生产
到目前为止,我们已经看到了一个完整的数据库驱动的微服务。然而,在现实世界中,一家公司可能拥有数百个微服务。它们必须由 Kubernetes 等云原生框架进行管理和编排。
WasmEdge 应用程序完全符合 OCI[27] 标准。它们可以在 Docker Hub 或其他 Open Container Initiative 存储库中进行管理和存储。通过 crun 集成[28],WasmEdge 可以在同一个 Kubernetes 集群中与 Linux 容器应用程序并行运行。这个存储库展示了如何在流行的容器工具链中运行 WasmEdge 应用程序,包括 CRI-O、containerd、Kubernetes、Kind、OpenYurt、KubeEdge 等。
Kubernetes 堆栈中的 WebAssembly 运行时
此外,微服务通常与服务框架一起部署。例如,Dapr[29] 是一个流行的微服务运行时框架。它为每个微服务提供一个“sidecar”服务。微服务通过 Dapr API 访问 Sidecar,以发现和调用网络上的其他服务、管理状态数据和访问消息队列。WASI(WebAssembly 系统接口)的 Dapr SDK[30] 使基于 WasmEdge 的微服务能够访问其附加的 Dapr sidecar。我们也提供了 一个完整的演示应用程序[31],这个 demo 有一个 Jamstack 静态 Web 前端、三个微服务和一个数据库服务。
结论本文中,我们讨论了为什么 WebAssembly 是基于 Rust 的服务端应用程序的出色运行时沙盒格式。我们通过具体的代码示例演示了如何创建 HTTP Web 服务、使用其他 Web 服务、从 Rust 访问关系数据库,然后在 WasmEdge 中运行已编译的应用程序。我们还谈到了部署问题,例如 Kubernetes 和 Dapr 集成。
使用这些 crate 和模板应用程序,你将能够在 Rust 中构建自己的轻量级微服务!除了微服务之外,WasmEdge Runtime 还可以在许多不同的场景中广泛用作应用程序沙箱:
- flows.network[32] 是一个用于 SaaS 自动化的 Serverless 平台。用户可以使用 Rust 和 WebAssembly 为 SaaS 产品创建机器人、自定义和连接器;
- 可以在 WebAssembly 中为数据库创建 用户定义函数[33] (UDF) 和 Extract-Transform-Load (ETL) 函数,并拥有这些以“Serverless”方式嵌入或与数据库共存的函数;
- 可以用 WebAssembly 为边缘设备创建可移植且高性能的应用程序,运行 Android[34]、Open Harmony,甚至 seL4[35] RTOS;
- WebAssembly 广泛用作区块链执行智能合约的运行时。例如,WasmEdge 为 Polkadot、FileCoin 和 XRP(Ripple)网络运行节点和智能合约。下一代以太坊区块链 VM,Ewasm[36] 也是基于 WebAssembly;
了解有关 WasmEdge 的更多信息,请参阅官方文档[37]。
参考资料[1]
评为最受欢迎的编程语言: https://survey.stackoverflow.co/2022/
[2]
被Linux内核接受: https://thenewstack.io/rust-in-the-linux-kernel/
[3]
包括服务端应用程序: https://blog.logrocket.com/template-rendering-in-rust/
[4]
WebAssembly: https://webassembly.org/
[5]
WasmEdge Runtime: https://github.com/WasmEdge/WasmEdge
[6]
发表在 IEEE Software 上的研究: https://arxiv.org/abs/2010.07115
[7]
准备工作: https://blog.logrocket.com/rust-microservices-server-side-webassembly/#prerequisites
[8]
创建 Web 服务: https://blog.logrocket.com/rust-microservices-server-side-webassembly/#creating-a-web-service
[9]
创建 Web 服务客户端: https://blog.logrocket.com/rust-microservices-server-side-webassembly/#creating-a-web-service-client
[10]
创建数据库客户端: https://blog.logrocket.com/rust-microservices-server-side-webassembly/#creating-a-database-client
[11]
构建、运行和部署微服务: https://blog.logrocket.com/rust-microservices-server-side-webassembly/#building-running-and-deploying-the-microservice
[12]
进入生产: https://blog.logrocket.com/rust-microservices-server-side-webassembly/#going-to-production
[13]
tokio: https://github.com/WasmEdge/tokio
[14]
mio: https://github.com/WasmEdge/mio
[15]
hyper: https://github.com/WasmEdge/hyper
[16]
microservice-rust-mysql: https://github.com/second-state/microservice-rust-mysql
[17]
hyper: https://hyper.rs/
[18]
reqwest: https://github.com/WasmEdge/reqwest
[19]
http_req: https://github.com/second-state/http_req
[20]
myql: https://github.com/WasmEdge/rust-mysql-simple-wasi
[21]
mysql_async: https://github.com/WasmEdge/mysql_async_wasi
[22]
microservice-rust-mysql: https://github.com/second-state/microservice-rust-mysql
[23]
mariaDB: https://mariadb.org/
[24]
安装 Rust 编译器: https://www.rust-lang.org/tools/install
[25]
安装 WasmEdge Runtime: https://wasmedge.org/book/en/quick_start/install.html
[26]
安装 MySQL 数据库服务器: https://dev.mysql.com/doc/mysql-getting-started/en/
[27]
OCI: https://opencontainers.org/
[28]
crun 集成: https://opensource.com/article/22/10/wasm-containers
[29]
Dapr: https://dapr.io/
[30]
Dapr SDK: https://github.com/second-state/dapr-sdk-wasi
[31]
一个完整的演示应用程序: https://github.com/second-state/dapr-wasm
[32]
flows.network: https://flows.network/
[33]
用户定义函数: https://www.secondstate.io/articles/udf-saas-extension/
[34]
Android: https://wasmedge.org/book/en/contribute/build_from_src/android.html
[35]
seL4: https://www.secondstate.io/articles/wasmedge-sel4/
[36]
Ewasm: https://ewasm.readthedocs.io/en/mkdocs/
[37]
官方文档: https://wasmedge.org/book/en/
WasmEdge将于11月9日在硅谷举办Wasm Meetup,戳此报名和来自Docker、Dapr、字节等的技术大佬交流
https://www.meetup.com/sf-software-development-meetup-group/events/289224769/