什么是 Tower,为什么需要它?
\n\nTower 是 Rust 生态系统中的一个基础库,提供了用于构建模块化、可组合的异步网络服务和中间件(middleware)的抽象。它不是一个现成的 HTTP 框架(如 Actix-web 或 Axum),而是它们的“构建材料”。例如,Axum 就是构建在 Tower 之上的。
\n\nTower 的核心思想是 Service 特征(trait),它代表一个异步函数,将请求(Request)转换为响应(Response)。Tower 中的中间件(或“Layer”)是一种将一个服务包装在另一个服务中的方式,从而在不修改原始服务代码的情况下,为其添加额外的行为(如日志记录、身份验证、超时、负载均衡等)。
为什么需要它?
\n- \n
- 模块化:您可以将复杂的请求处理逻辑分解为小型、独立、可重用的组件(服务和中间件)。 \n
- 可组合性:中间件可以轻松组合,像层(layers)一样相互叠加。 \n
- 异步性:Tower 专为与异步 Rust(tokio、async-std)一起工作而设计,并支持 futures。 \n
- 可测试性:每个组件都可以独立测试。 \n
安装 Tower
\n\nTower 是一个 Rust 库,因此通过包管理器 Cargo 安装。将依赖项添加到您的 Cargo.toml 文件中:
[dependencies]\ntower = { version = "0.4", features = ["full"] } # "full" 包含所有功能\ntokio = { version = "1", features = ["full"] } # 用于异步运行时\n\n\n之后,在终端中执行以下命令:
\ncargo build\n\n该库将与您的项目一起下载并编译。
\n\n快速入门:您的第一个服务和中间件
\n\n让我们创建一个简单的服务,它接收一个字符串并返回相同的大写字符串。然后,将其包装在一个记录每个请求的中间件中。
\n\nuse std::convert::Infallible;\nuse std::task::{Context, Poll};\nuse tower::{Service, Layer, ServiceBuilder};\nuse tower::layer::util::Identity;\nuse std::future::{Ready, ready};\n\n// 1. 定义我们的服务\n#[derive(Clone)]\nstruct MyService;\n\nimpl<Request> Service<Request> for MyService {\n type Response = String;\n type Error = Infallible;\n type Future = Ready<Result<Self::Response, Self::Error>>;\n\n fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {\n Poll::Ready(Ok(()))\n }\n\n fn call(&mut self, req: Request) -> Self::Future {\n // 直接返回一个字符串\n ready(Ok(format!("Hello from service! Request: {:?}", std::any::type_name::<Request>())))\n }\n}\n\n// 2. 定义用于日志记录的中间件\n#[derive(Clone)]\nstruct LogMiddleware;\n\nimpl<S> Layer<S> for LogMiddleware {\n type Service = LogService<S>;\n\n fn layer(&self, inner: S) -> Self::Service {\n LogService { inner }\n }\n}\n\n#[derive(Clone)]\nstruct LogService<S> {\n inner: S,\n}\n\nimpl<S, Request> Service<Request> for LogService<S>\nwhere\n S: Service<Request>,\n S::Future: std::marker::Send,\n Request: std::fmt::Debug,\n{\n type Response = S::Response;\n type Error = S::Error;\n type Future = S::Future;\n\n fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {\n self.inner.poll_ready(cx)\n }\n\n fn call(&mut self, req: Request) -> Self::Future {\n printl