Last active
October 24, 2025 14:01
-
-
Save jexjws/620968a668118885342e117f40a7a9b1 to your computer and use it in GitHub Desktop.
在 Y 分钟内学习 motore
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| * Motore: 一个受 Tower 启发的 Rust 异步中间件抽象库 | |
| * | |
| * 核心理念: | |
| * 1. `Service`: 代表一个异步服务 (Request -> Response)。 | |
| * 2. `Layer`: 代表中间件,它包装一个 `Service` 并返回一个新的 `Service`。 | |
| * 3. `Cx`: 一个可变的上下文,在整个调用链中传递,你可以使用它在整个调用链中传递数据(比如数据库连接、tracing span、用户信息等),这可是 Motore 的一大特色,也是与 Tower 的主要区别之一。 | |
| */ | |
| // ----------------------------------------------------------------------------- | |
| // 1. 核心抽象:`Service` Trait (推荐方式:使用宏) | |
| // ----------------------------------------------------------------------------- | |
| // `motore` 的核心是 `Service` trait (定义于 motore/src/service/mod.rs)。 | |
| // 它代表一个接收 `Cx` 上下文和 `Request`,并异步返回 `Response` 的服务。 | |
| // `motore-macros/src/lib.rs` 提供了 `#[motore::service]` 宏, | |
| // 这是我们推荐的,实现 `Service` trait 最便捷的方式。 | |
| use motore::service; | |
| use motore::service::Service; | |
| // 我们定义一个上下文(Context) | |
| #[derive(Debug, Clone)] | |
| struct MyContext { | |
| request_id: u32, | |
| processing_steps: u32, // 示例:一个可写的上下文状态 | |
| } | |
| struct MyMacroService; | |
| // --- 使用宏 `#[service]` 实现 `Service` --- | |
| #[service] | |
| impl Service<MyContext, String> for MyMacroService { | |
| async fn call(&self, cx: &mut MyContext, req: String) -> Result<String, Infallible> { | |
| // --- 演示对 &mut Cx 的修改 --- | |
| cx.processing_steps += 1; | |
| println!("[MacroService] handling req id: {}, step: {}", cx.request_id, cx.processing_steps); | |
| let res = Ok(req.to_uppercase()); | |
| println!("[MacroService] responding req id: {}, step: {}", cx.request_id, cx.processing_steps); | |
| res | |
| } | |
| } | |
| // ----------------------------------------------------------------------------- | |
| // 2. 深入理解:`Service` Trait | |
| // ----------------------------------------------------------------------------- | |
| // 其实 `#[service]` 宏在背后, | |
| // - 自动从 `Result<String, Infallible>` 推断出: | |
| // - `type Response = String;` | |
| // - `type Error = Infallible;` | |
| // - 自动将 `async fn call` 转换为 trait 要求的 `fn call(...) -> impl Future` 签名 | |
| // - 自动将函数体包装在 `async move { ... }` 块中 | |
| // 最后,宏把你刚才实现的 Service 转换成了 `motore/src/service/mod.rs` 中真正的核心 `Service` trait | |
| /* | |
| pub trait Service<Cx, Request> { | |
| /// Service 处理成功时返回的响应类型 | |
| type Response; | |
| /// Service 处理失败时返回的错误类型 | |
| type Error; | |
| /// 核心方法:处理请求并异步返回响应 | |
| /// 注意这个签名:它 *不* 是 `async fn call`。 | |
| /// 它是一个返回 `impl Future` 的普通函数 (RPITIT 风格)。 | |
| fn call( | |
| &self, | |
| cx: &mut Cx, | |
| req: Request, | |
| ) -> impl std::future::Future<Output = Result<Self::Response, Self::Error>> + Send; | |
| } | |
| */ | |
| // 因为它定义的是 `fn call(...) -> impl Future`, | |
| // 如果不使用宏的话,你就得 *手动* 匹配这个签名: | |
| use std::convert::Infallible; | |
| use std::future::Future; | |
| // 这是我们的“业务逻辑”服务 | |
| struct MyManualService; | |
| // --- 不使用宏,手动实现 `Service` --- | |
| // | |
| // 这非常繁琐,你需要: | |
| // 1. 明确定义 `type Response` | |
| // 2. 明确定义 `type Error` | |
| // 3. 编写正确的 `fn call(...) -> impl Future` 签名 | |
| // 4. 在 `call` 内部返回一个 `async move { ... }` 块 | |
| // | |
| // 这正是 `#[service]` 宏帮你自动完成的工作! | |
| impl Service<MyContext, String> for MyManualService { | |
| type Response = String; | |
| type Error = Infallible; // Infallible 表示这个服务永远不会失败 | |
| // 手动实现 `call` | |
| fn call( | |
| &self, | |
| cx: &mut MyContext, | |
| req: String, | |
| ) -> impl Future<Output = Result<Self::Response, Self::Error>> + Send { | |
| // 在本例中,我们只读取上下文,不修改 | |
| println!("[ManualService] handling req id: {}, step: {}", cx.request_id, cx.processing_steps); | |
| // 你必须返回一个实现了 Future 的东西,通常是一个 async 块 | |
| async move { | |
| let res = Ok(req.to_uppercase()); | |
| println!("[ManualService] responding req id: {}, step: {}", cx.request_id, cx.processing_steps); | |
| res | |
| } | |
| } | |
| } | |
| // 结论:宏极大地简化了 Service 的实现,让你专注于 `async fn` 业务逻辑。而不是 `impl Future` 的 trait 签名模板。 | |
| // ----------------------------------------------------------------------------- | |
| // 3. 中间件:`Layer` Trait | |
| // ----------------------------------------------------------------------------- | |
| use motore::layer::Layer; | |
| // `Layer` (来自 `motore/src/layer/mod.rs`) 是一个工厂, | |
| // 它接收一个内部服务 `S` (inner),并返回一个包装后的新服务 `Self::Service`。 | |
| /* | |
| pub trait Layer<S> { | |
| /// 包装后返回的新 Service 类型 | |
| type Service; | |
| /// 将内部服务 S 包装成新服务 Self::Service | |
| fn layer(self, inner: S) -> Self::Service; | |
| } | |
| */ | |
| // --- 实现一个 `Layer` (日志中间件) --- | |
| // 这是 Layer 的标准模式:"两个 Struct" | |
| // 1. `LogLayer`: Layer 本身 (工厂) | |
| #[derive(Clone)] | |
| struct LogLayer { | |
| target: &'static str, | |
| } | |
| // 2. `LogService<S>`: Layer 返回的新 Service (包装器) | |
| #[derive(Clone)] | |
| struct LogService<S> { | |
| inner: S, // 内部服务 | |
| target: &'static str, | |
| } | |
| // 实现 `Layer` trait | |
| impl<S> Layer<S> for LogLayer { | |
| type Service = LogService<S>; // 指定返回类型 | |
| fn layer(self, inner: S) -> Self::Service { | |
| // 返回包装后的新 Service | |
| LogService { | |
| inner, | |
| target: self.target, | |
| } | |
| } | |
| } | |
| // --- 手动实现 `LogService` 的 `Service` trait --- | |
| // | |
| // 同样,这很繁琐。 | |
| impl<Cx, Req, S> Service<Cx, Req> for LogService<S> | |
| where | |
| // `S` 必须也是一个 Service,并且满足 Send/Sync 等约束 | |
| S: Service<Cx, Req> + Send + Sync, | |
| S::Response: Send, | |
| S::Error: Send, | |
| Cx: Send, // LogService 是通用的,它不关心 Cx 的具体类型 | |
| Req: Send, | |
| { | |
| // 响应和错误类型通常与内部服务相同 | |
| type Response = S::Response; | |
| type Error = S::Error; | |
| fn call( | |
| &self, | |
| cx: &mut Cx, | |
| req: Req, | |
| ) -> impl Future<Output = Result<Self::Response, Self::Error>> + Send { | |
| println!("[LogLayer] (Manual) target: {}, enter", self.target); | |
| // 必须返回 async 块 | |
| async move { | |
| // 在调用内部服务之前执行逻辑 | |
| // 调用内部服务 | |
| let result = self.inner.call(cx, req).await; | |
| // 在内部服务返回之后执行逻辑 | |
| match &result { | |
| Ok(_) => println!("[LogLayer] (Manual) target: {}, exit (Ok)", self.target), | |
| Err(_) => println!("[LogLayer] (Manual) target: {}, exit (Err)", self.target), | |
| } | |
| result | |
| } | |
| } | |
| } | |
| // ----------------------------------------------------------------------------- | |
| // 4. 使用宏实现 `Layer` 的 `Service` 部分 | |
| // ----------------------------------------------------------------------------- | |
| // 我们可以对 `LogService<S>` 的 `impl` 块也使用宏 | |
| // (注意:`Layer` trait 的 `impl` 块保持不变,宏只用于 `Service` trait) | |
| #[derive(Clone)] | |
| struct LogServiceMacro<S> { | |
| inner: S, | |
| target: &'static str, | |
| } | |
| // (`impl Layer` 部分省略,和上面一样,它返回 `LogServiceMacro<S>`) | |
| // --- 使用宏实现 `LogService` --- | |
| #[service] | |
| impl<Cx, Req, S> Service<Cx, Req> for LogServiceMacro<S> | |
| where | |
| S: Service<Cx, Req> + Send + Sync, // 内部服务约束 | |
| Cx: Send + 'static, | |
| Req: Send + 'static, | |
| { | |
| // 再次,我们只需要写 `async fn` | |
| // 宏会自动推断 `Response = S::Response` 和 `Error = S::Error` | |
| async fn call(&self, cx: &mut Cx, req: Req) -> Result<S::Response, S::Error> { | |
| println!("[LogLayer] (Macro) target: {}, enter", self.target); | |
| // 逻辑完全相同,但代码更清晰 | |
| let result = self.inner.call(cx, req).await; | |
| match &result { | |
| Ok(_) => println!("[LogLayer] (Macro) target: {}, exit (Ok)", self.target), | |
| Err(_) => println!("[LogLayer] (Macro) target: {}, exit (Err)", self.target), | |
| } | |
| result | |
| } | |
| } | |
| // ----------------------------------------------------------------------------- | |
| // 5. 组合:`ServiceBuilder` | |
| // ----------------------------------------------------------------------------- | |
| use motore::builder::ServiceBuilder; | |
| use motore::timeout::TimeoutLayer; // Motore 自带的 Layer (motore/src/timeout.rs) | |
| use std::time::Duration; | |
| // `ServiceBuilder` (来自 `motore/src/builder.rs`) | |
| // 允许你将多个 Layer 组合到一个 Service 上。 | |
| async fn run_builder() { | |
| // 1. 创建一个 ServiceBuilder | |
| let builder = ServiceBuilder::new() | |
| // 2. 添加 Layer。 | |
| // 请求的执行顺序:从上到下 | |
| // 响应的执行顺序:从下到上 | |
| .layer(LogLayer { target: "Outer" }) | |
| .layer(TimeoutLayer::new(Some(Duration::from_secs(1)))) // Motore 提供的 | |
| .layer(LogLayer { target: "Inner" }); | |
| // 3. 将 Layer 栈应用到一个“最内部”的服务上 | |
| // 这里我们使用 `MyMacroService` 作为最核心的业务服务 | |
| let service = builder.service(MyMacroService); | |
| // 4. 准备上下文和请求 | |
| // 注意:processing_steps 从 0 开始 | |
| let mut cx = MyContext { request_id: 42, processing_steps: 0 }; | |
| let req = "hello motore".to_string(); | |
| // 5. 调用! | |
| let res = service.call(&mut cx, req).await; | |
| println!("\nFinal response: {:?}", res); | |
| /* | |
| * 预期输出: | |
| * | |
| * [LogLayer] (Manual) target: Outer, enter | |
| * [LogLayer] (Manual) target: Inner, enter | |
| * [MacroService] handling req id: 42, step: 1 <-- step 变为 1 | |
| * [MacroService] responding req id: 42, step: 1 | |
| * [LogLayer] (Manual) target: Inner, exit (Ok) | |
| * [LogLayer] (Manual) target: Outer, exit (Ok) | |
| * | |
| * Final response: Ok("HELLO MOTORE") | |
| */ | |
| // 最终,原始的 cx 已经被修改 | |
| println!("Final context steps: {}", cx.processing_steps); // 将打印 1 | |
| } | |
| // ----------------------------------------------------------------------------- | |
| // 6. 辅助工具:`service_fn` | |
| // ----------------------------------------------------------------------------- | |
| // 有时候你不想为简单的服务创建一个新 struct。 | |
| // `motore/src/service/service_fn.rs` 提供了 `service_fn`,它能把符合要求的函数直接转换成一个 `Service`。 | |
| use motore::service::service_fn; | |
| async fn my_handler_func(cx: &mut MyContext, req: String) -> Result<String, Infallible> { | |
| // --- 演示对 &mut Cx 的修改 --- | |
| cx.processing_steps += 10; | |
| println!("[service_fn] handling req id: {}, step: {}", cx.request_id, cx.processing_steps); | |
| Ok(req.to_lowercase()) | |
| } | |
| #[tokio::main] | |
| async fn main() { | |
| println!("\n--- 示例 1: 运行 `run_builder` ---"); | |
| run_builder().await; | |
| println!("\n--- 示例 2: 运行 `service_fn` (独立) ---"); | |
| // `service_fn` 可以将一个符合 `async fn(&mut Cx, Req) -> Result<Res, Err>` 签名 | |
| // 的函数或闭包,直接转换成一个 `Service`。 | |
| let fn_service = service_fn(my_handler_func); | |
| // 我们也运行一下它,来证明它是工作的 | |
| let mut cx1 = MyContext { request_id: 101, processing_steps: 0 }; | |
| let res1 = fn_service.call(&mut cx1, "HELLO WORLD".to_string()).await; | |
| // 检查修改后的上下文 | |
| println!("service_fn 响应: {:?}, context steps: {}", res1, cx1.processing_steps); // 打印 10 | |
| println!("\n--- 示例 3: 运行 `service_fn` (在 Builder 中) ---"); | |
| // 你也可以在 ServiceBuilder 中使用它: | |
| let service_from_fn = ServiceBuilder::new() | |
| .layer(LogLayer { target: "ServiceFn" }) | |
| .service_fn(my_handler_func); // .service(service_fn(my_handler_func)) 的简写 | |
| // 运行它 | |
| let mut cx2 = MyContext { request_id: 202, processing_steps: 0 }; | |
| let res2 = service_from_fn.call(&mut cx2, "ANOTHER EXAMPLE".to_string()).await; | |
| // 检查修改后的上下文 | |
| println!("service_from_fn 响应: {:?}, context steps: {}", res2, cx2.processing_steps); // 打印 10 | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment