helloworld service
从上一篇我们知道 service_fn 最终返回的是ServiceFn 结构体,并且实现了 Service trait, 所以,如果我们不想用service_fn 来实现handler,想自己实现一个Service 结构体,那我们只要构造一个 struct来实现 Service trait就行
struct HelloWorld;impl Service<Request<hyper::Body>> for HelloWorld {type Response = Response<Body>;type Error = Infallible;type Future = Ready<Result<Self::Response, Self::Error>>; // 这里为了简单,我们直接用future::Ready<T>// poll_ready可以用来做限流fn poll_ready(&mut self,cx: &mut std::task::Context<'_>,) -> std::task::Poll<Result<(), Self::Error>> {// 这里我们不需要限流,直接返回Okstd::task::Poll::Ready(Ok(()))}// 这里通过ready函数直接返回Readyfn call(&mut self, req: Request<Body>) -> Self::Future {ready(Ok(Response::new(Body::from("hello world"))))}}let make_service = make_service_fn(|_conn| async {Ok::<_, Infallible>(service_fn(handle))});// 将service_fn 改成 HelloWorldlet make_service = make_service_fn(|_conn| async {Ok::<_, Infallible>(HelloWorld)});
do some logging
接下来我们想在call 前后加先日志来统计执行的时间,很显然我们会想到在call函数前后加上日志,在这个例子中,这样做是可以的,因为这里的ready不是真正的async函数,但是假如说,我们在这里执行了一个rpc,或者http请求,或者io请求,那么这样就有问题了,因为实际上当”after async call”执行的时候,fu 还未真正开始执行,因为这里我们只是得到了一个future实例,future的poll函数并未执行,future只有在await它时才会执行
fn call(&mut self, req: Request<Body>) -> Self::Future {info!("before async call");let fu = ready(Ok(Response::new(Body::from("hello world"))));info!("after async call");fu}
那我们要如何在call函数中执行future呢?套个async是不是就可以await了?
fn call(&mut self, req: Request<Body>) -> Self::Future {async {log::info!("before");let resp = ready(Ok(Response::new(Body::from("hello world")))).await;log::info!("after");resp}}

看到ide开始飘红就感到不好了🤷🏻♀️ , cargo check看了下错误日志,因为 async block 编译后返回的是一个opaque type,不是 future::Ready类型的
error[E0308]: mismatched types--> src/main.rs:63:9|62 | fn call(&mut self, req: Request<Body>) -> Self::Future {| ------------ expected `futures::future::Ready<Result<Response<Body>, Infallible>>` because of return type63 | / async {64 | | log::info!("before");65 | | let resp = ready(Ok(Response::new(Body::from("hello world")))).await;66 | | log::info!("after");67 | | resp68 | | }| |_________^ expected struct `futures::future::Ready`, found opaque type|::: /Users/sean/.rustup/toolchains/stable-x86_64-apple-darwin/lib/rustlib/src/rust/library/core/src/future/mod.rs:65:43|65 | pub const fn from_generator<T>(gen: T) -> impl Future<Output = T::Return>| ------------------------------- the found opaque type|= note: expected struct `futures::future::Ready<Result<Response<Body>, Infallible>>`found opaque type `impl futures::Future<Output = [async output]>`
box pin
impl Service<Request<Body>> for HelloWorld {type Response = Response<Body>;type Error = Infallible;type Future = BoxFuture<'static, Result<Self::Response, Self::Error>>;fn poll_ready(&mut self,cx: &mut std::task::Context<'_>,) -> std::task::Poll<Result<(), Self::Error>> {std::task::Poll::Ready(Ok(()))}fn call(&mut self, req: Request<Body>) -> Self::Future {Box::pin(async move {log::info!("before");let resp = ready(Ok(Response::new(Body::from("hello world")))).await;log::info!("after");resp})}}
用Box::pin 把async 包起来, 返回的是一个 Pin
impl Service<Request<Body>> for HelloWorld {type Response = Response<Body>;type Error = Infallible;// 类型改为 BoxFuturetype Future = BoxFuture<'static, Result<Self::Response, Self::Error>>;fn poll_ready(&mut self,cx: &mut std::task::Context<'_>,) -> std::task::Poll<Result<(), Self::Error>> {std::task::Poll::Ready(Ok(()))}fn call(&mut self, req: Request<Body>) -> Self::Future {// 用Box::pin 包装起来Box::pin(async move {log::info!("before");let resp = ready(Ok(Response::new(Body::from("hello world")))).await;log::info!("after");resp})}}
logging middleware
通常我们希望用一个middleware来做请求日志,而不是在每个service中记录日志
#[derive(Clone, Copy)]struct Logging<S> {inner: S,}impl<S> Logging<S> {fn new(inner: S) -> Self {Self { inner }}}impl<S, B> Service<Request<B>> for Logging<S>whereS: Service<Request<B>> + Clone + Send + 'static, // 我们限制S必须实现Service<Request> traitB: HttpBody + Send + 'static, // B 必须实现 HttpBodyS::Future: Send,{type Response = S::Response;type Error = S::Error;type Future = BoxFuture<'static, Result<Self::Response, Self::Error>>;fn poll_ready(&mut self,cx: &mut std::task::Context<'_>,) -> std::task::Poll<Result<(), Self::Error>> {self.inner.poll_ready(cx) // 直接代理到inner的 poll_ready上}fn call(&mut self, req: Request<B>) -> Self::Future {let mut inner = self.inner.clone(); // 这里要clone因为 我们约束了 Self::Future 必须是'static的Box::pin(async move {let method = req.method().clone();let path = req.uri().path().to_string();log::debug!("process req {} {}", method, path);let response = inner.call(req).await; // 这里调用inner service call函数// 如果我们在这里使用 self.inner.call() 则代表,Self::Future borrow 了 self,但我们要求返回的Future// 是'static的, 表示这个Future不会borrow anything,所以这里要将inner变成一个owned value, 可以move into Futurelog::debug!("finish process req {} {}", method, path);response})}}
使用logging middleware
// 将service_fn 改成 Logging::new(HelloWorld)let make_service = make_service_fn(|_conn| async {Ok::<_, Infallible>(Logging::new(HelloWorld))});
这样即实现了一个logging middleware service了
小结
我们通过实现Service trait 做到了logging middleware,但是并不完美,因为用Box::pin 包装之后每次都会在堆上分配,像是logging这样的middleware每次请求都要分配一次对性能是有影响的,rust的优势也无法体现,所以我们需要实现一个lowlevel Future
