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(本地回環),端口 3000let 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, // 提取路徑參數中的 IDname: "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, // 模擬自動生成的 IDname: 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,測試用 3443println!("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. 啟動服務器時綁定 Handleaxum_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 優先級等。