揭祕 Rust HTTP 生態系統
在這篇文章中,我們將看到 Web 開發的不同部分如何在 Rust HTTP 生態系統中組合在一起,這樣你就可以無所畏懼地在 Rust 中啓動你的下一個 (微)web 服務,並享受所有附帶的好處 (枚舉、一流的錯誤處理、無與倫比的可靠性、性能等等)。
Actix-web vs Axum
Rust HTTP 框架有兩個主要框架:acitx-web 和 axum。
兩者之間的主要區別在於,actix-web 實現了自己的 HTTP 技術棧,由於現代 HTTP 技術棧的瘋狂複雜性,這帶來了一些風險,而 axum 是對 HTTP 技術棧的人體工程學包裝,這被許多人認爲是最可靠和性能最好的 HTTP 技術棧。
axum 是由 Tokio 團隊維護的,並且是 hyper 的包裝器,所以我認爲它是更安全的選擇。
Rust HTTP 的不同層
既然我們已經選擇了 axum web 框架,那麼讓我們深入探討這個主題的核心:運行 HTTP 服務器所需的許多不同的 crate 是如何組合在一起的。
在技術棧的底部,你可以找到異步運行時:Tokio。運行時負責調度不同的任務,並提供操作系統套接字 API 的異步版本。在這裏,Tokio 監聽並接受新的 TCP 連接。
然後是 TLS 層,負責保護髮送和接收的數據流。這一層實際上由兩部分組成:tokio_*(如 tokio_rustls 或 tokio_boring) 和一些實現 TLS 協議的庫。
有幾個 crate 可以用來處理 TLS 協議:rustls、boring 和 OpenSSL。建議使用 rustls,因爲它是一個 rust 原生庫,它是靜態鏈接到你的可執行文件,當部署你的服務器 (服務器的 VM/Docker 鏡像是否包含正確版本的 OpenSSL…) 或當試圖交叉編譯你的程序時,你會遇到更少的問題。
tokio_rustls 將 tokio 類型和 rustls 類型打包到一個統一的層中。具體來說,它將 Tokio 的 TcpStream 轉換爲處理數據加密和解密的 TlsStream。每個 TLS 庫都有其等效的 tokio_*,例如 tokio_boring。
一旦有了 TLS 流之後,需要將其傳送到 hyper 中。Hyper 是整個團隊的大腦。它將來自網絡的字節轉換爲請求和響應結構。Hyper 與傳輸無關,它只是將字節流轉換爲結構化的請求和響應。
TLS 代碼示例:
let server_tls_config = rustls::ServerConfig::builder(); // ...
let tls_acceptor = tokio_rustls::TlsAcceptor::from(Arc::new(server_tls_config));
let listener = tokio::net::TcpListener::bind(SocketAddr::from(([127, 0, 0, 1], 8080))).await?;
loop {
let (stream, remote_socket_addr) = listener.accept().await?;
let tls_acceptor = tls_acceptor.clone();
let request_handler = hyper::service::service_fn(move |mut req| {
let remote_socket_addr = remote_socket_addr.to_string();
req.extensions_mut().insert(socket_addr_str);
actual_handler(req)
});
tokio::spawn(async move {
let tls_stream = tls_acceptor.accept(stream).await.unwrap(); // need to handle error
if let Err(err) = hyper_util::server::conn::auto::Builder::new(TokioExecutor::new())
.serve_connection_with_upgrades(TokioIo::new(tls_stream), request_handler)
.await
{
error!("failed to serve connection: {err:#}");
}
})
}
爲此,它依賴於 “編解碼器” 來解碼來自網絡的實際字節,這取決於 HTTP 協議的不同版本:HTTP 對應 HTTP/1.X,h2 用於 HTTP/2.X 等等。
在 hyper 之上,有 axum,人體工程學層提供了程序員在開發 web 服務器時所期望的實用工具:路由、中間件、錯誤處理,“提取器” 等等。
在客戶端,reqwest 相當於 axum,並提供連接池、代理處理、主機名解析等功能。
然後還有無數的 crate,比如 tower,它試圖通過其服務和層特性爲 Rust HTTP 生態系統帶來統一的處理程序和中間件。
最後,最重要的一塊:可觀察性。在 Rust 中,社區已經聚集在 tracing crate 上,這是由 tokio 團隊維護的,該團隊提供了處理所有結構化日誌記錄和跟蹤需求的實用程序。
總結
用 Rust 構建 web 服務是令人愉快的,因爲它們比任何其他編程語言都要健壯得多。尤其是當你 6 個月後回頭看你的代碼時,你可以直接理解每行代碼背後的意圖,這要歸功於 Rust 的表達性:Option, mut 和所有權規則。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/w5O-Iu7c98b7o5CJpR5Qgg