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
文件,添加 axum
、axum-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
工具:
安装 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
- macOS:
生成证书:
在项目根目录执行以下命令,生成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 服务
- 启动服务器:
cargo run
→ 输出HTTPS 服务器已启动,监听地址:https://127.0.0.1:3443
。 - 测试 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);
- 实现服务器优雅关闭。
后续还可以增加以下内容:
- 错误处理:替换示例中的
unwrap()
,用thiserror
或anyhow
处理实际项目中的错误; - 生产配置:优化 TLS 配置(如启用 TLS 1.3、添加证书链)、调整服务器参数(如连接数限制);
- 扩展功能:结合 axum 的中间件(如日志、认证)、使用
axum-server
的from_tcp
从现有 TCP 监听创建服务器; - 性能优化:基于 hyper 的特性调整线程池、启用 HTTP/2 优先级等。