Rust+MySQL+CRUD微服务的模板项目


在此 repo 中,我们演示了一个用 Rust 编写并连接到 MySQL 数据库的微服务。它支持通过 HTTP 服务接口对数据库表进行 CRUD 操作。

微服务被编译成 WebAssembly (Wasm) 并在 WasmEdge Runtime 中运行,WasmEdge Runtime 是 Linux 容器中本机编译的 Rust 应用程序的安全轻量级替代方案。
WasmEdge Runtime 可以通过 Docker、Podman 以及几乎所有类型的 Kubernetes 等容器工具进行管理和编排。它还适用于 Dapr 等微服务管理框架。

最简单的入门方法是使用支持 Wasm 的 Docker Desktop 或 Docker CLI 版本。


然后,您只需键入一个命令。
docker compose up
这将构建 Rust 源代码,运行 Wasm 服务器,并启动 MySQL 后备数据库。它还会启动一个基本的 STATIC 网络界面(可在http://localhost:8090获得)。请参阅Dockerfiledocker-compose.yml文件。您可以直接跳转到CRUD 测试部分以与 Web 服务交互。

详细点击标题

服务器端 WebAssembly 中的 Rust 微服务
Rust 编程语言在过去几年中获得了主流采用。它一直被开发人员评为最受欢迎的编程语言,并已被Linux 内核接受。Rust 使开发人员能够编写与 C 程序一样快速和小巧的正确且内存安全的程序。它非常适用于需要高可靠性和性能的基础架构软件,包括服务器端应用程序。
然而,对于服务器端应用程序,Rust 也带来了一些挑战。Rust 程序被编译成本机机器代码,这在多租户云环境中不可移植且不安全。我们还缺乏在云中管理和编排原生应用程序的工具。

因此,服务器端 Rust 应用程序通常在 VM 或 Linux 容器内运行,这会带来显着的内存和 CPU 开销。这削弱了 Rust 在效率方面的优势,并且难以在资源受限的环境中部署服务,例如边缘数据中心和边缘云。
这个问题的解决方案是WebAssembly (Wasm)

作为网络浏览器内的安全运行时,Wasm 程序可以安全地隔离在自己的沙箱中。借助新一代 Wasm 运行时,例如 Cloud Native Computing Foundation 的WasmEdge Runtime,您现在可以在服务器上运行 Wasm 应用程序。您可以将 Rust 程序编译为 Wasm 字节码,然后将 Wasm 应用程序部署到云端。

根据发表在 IEEE Software 上的一项研究,与 Linux 容器中本地编译的 Rust 应用程序相比,Wasm 应用程序可以快 100 倍(尤其是在启动时)并且小 1/100。这使得它们特别适合资源受限的环境,例如边缘云。

Wasm 运行时沙箱的攻击面要小得多,并且提供比 Linux 容器更好的隔离。此外,Wasm 运行时可跨操作系统和硬件平台移植。一旦将 Rust 程序编译成 Wasm,它就可以在从开发到生产、从云端到边缘的任何地方运行。

WasmEdge 运行时支持异步和非阻塞网络套接字。您可以用 Rust 编写网络应用程序,将它们编译成 Wasm,然后在 WasmEdge Runtime 中运行它们。在 Rust 生态系统中,WasmEdge 支持以下内容:

后端服务
下面展示了如何在hyper中为WasmEdge创建一个网络服务器。网络服务器的主要监听循环如下:

 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)
        }
    }
}

现在我们有了一个用于网络服务的HTTP服务器

客户端
一个典型的网络服务也需要消费其他网络服务。通过tokio和/或mio crates,WasmEdge应用程序可以很容易地纳入网络服务的HTTP客户端。在WasmEdge中支持以下Rust crates。

  • 易于使用的HTTP客户端的reqwest crate
  • 用于HTTP和HTTPS客户端的http_req 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 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);


投入生产
到目前为止,我们已经看到一个完整的数据库驱动的微服务在运行。然而,在现实世界中,一个公司可能有数百个微服务。它们必须由云原生框架(如Kubernetes)管理和协调。

WasmEdge应用程序是完全符合OCI标准的。它们可以被管理和存储在Docker Hub或其他开放容器倡议的存储库。通过crun集成,WasmEdge可以在同一个Kubernetes集群中与Linux容器应用程序并排运行。这个资源库展示了如何在流行的容器工具链中运行WasmEdge应用程序,包括CRI-O、containerd、Kubernetes、Kind、OpenYurt、KubeEdge等。

除了微服务,WasmEdge Runtime 还可以在许多不同的场景中广泛用作应用程序沙箱: