从零开始使用 axum-server 构建 HTTP/HTTPS 服务

发布于:2025-09-12 ⋅ 阅读:(13) ⋅ 点赞:(0)

axum-server 是 Rust 生态中为 axum 框架设计的高性能服务器实现,基于 hyper(底层 HTTP 引擎)和 tower(服务抽象)构建,支持 HTTP/1、HTTP/2 及 HTTPS。本教程将从环境准备到实战功能,一步步带你掌握 axum-server 的使用。

1. 环境准备:安装 Rust 与工具链

首先需要搭建 Rust 开发环境,这是运行 axum-server 项目的基础。

步骤 1:安装 Rust

打开终端,执行官方安装脚本(适用于 Windows/macOS/Linux):

# 安装 Rust 工具链(包含 cargo、rustc 等)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

按照提示完成安装,最后执行以下命令让环境变量生效:

# Linux/macOS
source $HOME/.cargo/env
# Windows(PowerShell)
$env:Path += ";$HOME\.cargo\bin"

步骤 2:验证环境

执行以下命令,确认 Rust 与 Cargo 安装成功:

rustc --version  # 应显示 Rust 版本(建议 1.70+)
cargo --version  # 应显示 Cargo 版本

2. 第一个项目:Hello World 服务

我们从最简单的 HTTP 服务开始,实现 “访问指定地址返回 Hello World” 的功能。

步骤 1:创建新项目

打开终端,执行以下命令创建名为 axum-server-demo 的 Rust 项目:

cargo new axum-server-demo
cd axum-server-demo

步骤 2:添加依赖

修改项目根目录下的 Cargo.toml 文件,添加 axumaxum-server 和 tokio(异步运行时)的依赖:

[package]
name = "axum-server-demo"
version = "0.1.0"
edition = "2021"

[dependencies]
# axum 框架:用于定义路由和处理请求
axum = "0.7"
# axum-server:核心服务器实现
axum-server = "0.7"
# tokio:异步运行时(axum/axum-server 依赖异步)
tokio = { version = "1.0", features = ["full"] }
# 用于处理网络地址(SocketAddr)
std-sys = "0.1"  # 或直接使用 std 的 net 模块(无需额外依赖,本示例用 std)

步骤 3:编写核心代码

打开 src/main.rs 文件,替换为以下代码(关键步骤已加注释):

// 1. 导入所需模块
use axum::{routing::get, Router};  // axum 的路由与路由器
use axum_server::Server;          // axum-server 的核心服务器类型
use std::net::SocketAddr;         // 标准库的网络地址类型

// 2. 异步主函数(axum/axum-server 基于异步,需用 tokio::main 宏)
#[tokio::main]
async fn main() {
    // 3. 定义路由:访问根路径(/)时,用 GET 方法触发 handler
    // handler 是一个异步函数,返回 "Hello, axum-server!"
    let app = Router::new().route("/", get(|| async { "Hello, axum-server!" }));

    // 4. 定义服务器监听地址:127.0.0.1(本地回环),端口 3000
    let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
    println!("服务器已启动,监听地址:http://{}", addr);

    // 5. 创建并启动服务器
    // - bind(addr):绑定监听地址,生成 Server 实例
    // - serve(app.into_make_service()):将 axum 的 Router 转换为 tower 的 MakeService(axum-server 要求)
    // - await:异步等待服务器运行(阻塞主线程,直到服务器停止)
    // - unwrap():简化错误处理(生产环境需替换为 proper error handling)
    Server::bind(addr)
        .serve(app.into_make_service())
        .await
        .unwrap();
}

步骤 4:运行与测试

(1)启动服务器:在项目根目录执行以下命令:

cargo run

终端会输出:服务器已启动,监听地址:http://127.0.0.1:3000

(2)测试服务

  • 方法 1:打开浏览器,访问 http://127.0.0.1:3000,页面会显示 Hello, axum-server!
  • 方法 2:用终端执行 curl http://127.0.0.1:3000,会返回同样的字符串。

3. 进阶功能 1:多路由与请求处理

实际项目中需要多个路由,我们扩展示例,添加 “获取用户信息”“处理 POST 请求” 的功能。

步骤 1:更新代码(支持多路由与 JSON)

修改 src/main.rs,添加 JSON 处理依赖(需先在 Cargo.toml 中添加 serde):

# 在 Cargo.toml 的 [dependencies] 中添加
serde = { version = "1.0", features = ["derive"] }  # 用于 JSON 序列化/反序列化
axum::extract::Json = "0.7"  # axum 内置的 JSON 提取器(无需额外依赖,已包含在 axum 中)

然后更新 src/main.rs 代码:

use axum::{
    extract::Json,  // 提取 JSON 请求体
    routing::{get, post},  // 支持 GET 和 POST 方法
    Router,
};
use axum_server::Server;
use serde::Serialize;  // 用于序列化响应
use std::net::SocketAddr;

// 定义用户信息结构体(用于响应 JSON)
#[derive(Serialize)]
struct User {
    id: u32,
    name: String,
    email: String,
}

// 定义 POST 请求体结构体(用于接收客户端数据)
#[derive(serde::Deserialize)]
struct CreateUserRequest {
    name: String,
    email: String,
}

// 异步 handler:获取指定 ID 的用户信息(路径参数:id)
async fn get_user(id: axum::extract::Path<u32>) -> Json<User> {
    // 模拟从数据库获取用户(实际项目中替换为真实逻辑)
    let user = User {
        id: id.0,  // 提取路径参数中的 ID
        name: "Alice".to_string(),
        email: "alice@example.com".to_string(),
    };
    Json(user)  // 返回 JSON 格式的用户信息
}

// 异步 handler:创建用户(接收 JSON 请求体)
async fn create_user(Json(req): Json<CreateUserRequest>) -> Json<User> {
    // 模拟创建用户(实际项目中需保存到数据库)
    let new_user = User {
        id: 100,  // 模拟自动生成的 ID
        name: req.name,
        email: req.email,
    };
    Json(new_user)  // 返回创建后的用户信息
}

#[tokio::main]
async fn main() {
    // 定义多路由
    let app = Router::new()
        .route("/", get(|| async { "Hello, axum-server!" }))  // 根路径
        .route("/users/:id", get(get_user))  // 获取用户(路径参数 :id)
        .route("/users", post(create_user));  // 创建用户(POST 请求)

    let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
    println!("服务器已启动,监听地址:http://{}", addr);

    Server::bind(addr)
        .serve(app.into_make_service())
        .await
        .unwrap();
}

步骤 2:测试多路由

(1)启动服务器cargo run

(2)测试根路径curl http://127.0.0.1:3000 → 返回 Hello, axum-server!

(3)测试获取用户curl http://127.0.0.1:3000/users/123 → 返回 JSON:

{"id":123,"name":"Alice","email":"alice@example.com"}

(4)测试创建用户(POST 请求):

curl -X POST -H "Content-Type: application/json" -d '{"name":"Bob","email":"bob@example.com"}' http://127.0.0.1:3000/users

返回 JSON(创建后的用户):

{"id":100,"name":"Bob","email":"bob@example.com"}

4. 进阶功能 2:启用 HTTPS(基于 rustls)

实际项目中需用 HTTPS 保证安全,axum-server 支持基于 rustls 的 HTTPS,我们来实现它。

步骤 1:准备 TLS 证书

首先需要生成本地测试证书(生产环境需从 CA 机构申请),推荐用 mkcert 工具:

  1. 安装 mkcert

    • macOS:brew install mkcert
    • Windows:choco install mkcert(需先安装 Chocolatey)
    • Linux:sudo apt install libnss3-tools && curl -JLO "https://dl.filippo.io/mkcert/latest?for=linux/amd64" && chmod +x mkcert-v*-linux-amd64 && sudo mv mkcert-v*-linux-amd64 /usr/local/bin/mkcert
  2. 生成证书
    在项目根目录执行以下命令,生成 localhost.pem(证书)和 localhost-key.pem(私钥):

mkcert -install  # 安装本地 CA(仅首次需要)
mkcert localhost 127.0.0.1  # 生成针对本地地址的证书

步骤 2:添加 HTTPS 依赖

修改 Cargo.toml,添加 axum-server 的 tls-rustls 特性:

[dependencies]
# 其他依赖不变...
axum-server = { version = "0.7", features = ["tls-rustls"] }  # 启用 rustls 支持
rustls-pemfile = "1.0"  # 用于读取 PEM 格式的证书/私钥

步骤 3:修改代码启用 HTTPS

更新 src/main.rs,替换服务器启动逻辑为 HTTPS 版本:

// 新增导入
use axum_server::tls_rustls::RustlsConfig;
use rustls_pemfile::{certs, pkcs8_private_keys};
use std::fs::File;
use std::io::BufReader;

// 其他代码(路由、handler)不变...

#[tokio::main]
async fn main() {
    // 1. 读取 TLS 证书和私钥
    let cert_file = File::open("localhost.pem").unwrap();  // 证书文件路径
    let key_file = File::open("localhost-key.pem").unwrap();  // 私钥文件路径

    // 2. 解析证书(PEM 格式)
    let cert_chain = certs(&mut BufReader::new(cert_file))
        .unwrap()
        .into_iter()
        .map(|cert| rustls::Certificate(cert))
        .collect();

    // 3. 解析私钥(PKCS8 格式)
    let private_key = pkcs8_private_keys(&mut BufReader::new(key_file))
        .unwrap()
        .into_iter()
        .next()
        .unwrap();
    let private_key = rustls::PrivateKey(private_key);

    // 4. 创建 Rustls 配置
    let tls_config = RustlsConfig::builder()
        .with_single_cert(cert_chain, private_key)
        .unwrap();  // 生产环境需处理错误

    // 5. 定义路由(与之前一致)
    let app = Router::new()
        .route("/", get(|| async { "Hello, HTTPS!" }))
        .route("/users/:id", get(get_user))
        .route("/users", post(create_user));

    let addr = SocketAddr::from(([127, 0, 0, 1], 3443));  // HTTPS 常用端口 443,测试用 3443
    println!("HTTPS 服务器已启动,监听地址:https://{}", addr);

    // 6. 启动 HTTPS 服务器(用 bind_rustls 替代 bind)
    axum_server::bind_rustls(addr, tls_config)
        .serve(app.into_make_service())
        .await
        .unwrap();
}

步骤 4:测试 HTTPS 服务

  1. 启动服务器cargo run → 输出 HTTPS 服务器已启动,监听地址:https://127.0.0.1:3443
  2. 测试 HTTPS 路径
curl -k https://127.0.0.1:3443  # -k 忽略本地证书验证(测试用)

返回 Hello, HTTPS!,表示 HTTPS 服务正常运行。

5. 进阶功能 3:服务器生命周期控制(优雅关闭)

在生产环境中,需要让服务器 “优雅关闭”(处理完现有请求后再停止,避免数据丢失),axum-server 提供 Handle 类型实现此功能。

步骤 1:更新代码(添加优雅关闭逻辑)

修改 src/main.rs,关键新增 Handle 和信号监听:

// 新增导入:用于监听系统信号(如 Ctrl+C)
use axum_server::Handle;
use tokio::signal;
use tokio::sync::oneshot;

// 其他代码(路由、handler、TLS 配置)不变...

#[tokio::main]
async fn main() {
    // 1. 创建 Handle(用于控制服务器关闭)
    let handle = Handle::new();
    let shutdown_handle = handle.clone();  // 克隆用于信号监听任务

    // 2. 启动信号监听任务(独立于服务器,监听 Ctrl+C 或 SIGTERM)
    tokio::spawn(async move {
        // 监听系统中断信号(Ctrl+C)
        signal::ctrl_c().await.unwrap();
        println!("\n收到关闭信号,开始优雅关闭服务器...");
        
        // 触发服务器优雅关闭(等待现有请求处理完成)
        shutdown_handle.shutdown();
    });

    // 3. 定义路由和 TLS 配置(与之前一致)
    let app = Router::new()
        .route("/", get(|| async { "Hello, 优雅关闭!" }))
        .route("/users/:id", get(get_user))
        .route("/users", post(create_user));

    let addr = SocketAddr::from(([127, 0, 0, 1], 3443));
    let tls_config = RustlsConfig::builder()  // 复用之前的 TLS 配置逻辑
        .with_single_cert(cert_chain, private_key)
        .unwrap();

    // 4. 启动服务器时绑定 Handle
    axum_server::bind_rustls(addr, tls_config)
        .handle(handle)  // 将 Handle 传递给服务器
        .serve(app.into_make_service())
        .await
        .unwrap();

    println!("服务器已完全关闭");
}

步骤 2:测试优雅关闭

(1)启动服务器cargo run

(2)触发关闭:在终端按 Ctrl+C,会看到:

收到关闭信号,开始优雅关闭服务器...
服务器已完全关闭

(3)验证效果:如果在按 Ctrl+C 前发起一个慢请求(如模拟耗时处理),服务器会等待请求完成后再关闭,而非强制中断。

6. 总结与进阶方向

通过本教程,你已掌握 axum-server 的核心用法:

  • 搭建基础 HTTP 服务,实现多路由与 JSON 处理;
  • 启用 HTTPS(基于 rustls);
  • 实现服务器优雅关闭。

后续还可以增加以下内容:

  1. 错误处理:替换示例中的 unwrap(),用 thiserror 或 anyhow 处理实际项目中的错误;
  2. 生产配置:优化 TLS 配置(如启用 TLS 1.3、添加证书链)、调整服务器参数(如连接数限制);
  3. 扩展功能:结合 axum 的中间件(如日志、认证)、使用 axum-server 的 from_tcp 从现有 TCP 监听创建服务器;
  4. 性能优化:基于 hyper 的特性调整线程池、启用 HTTP/2 优先级等。

网站公告

今日签到

点亮在社区的每一天
去签到