Part 4. 添加一个中间件

接下来,让我们来看下如何给 Volo 添加一个中间件。

例如,我们需要一个中间件,打印出我们收到的请求、返回的响应以及消耗的时间,那我们可以在 lib.rs 中写这么一个 Service:

#[derive(Clone)]
pub struct LogService<S>(S);

#[volo::service]
impl<Cx, Req, S> volo::Service<Cx, Req> for LogService<S>
where
    Req: std::fmt::Debug + Send + 'static,
    S: Send + 'static + volo::Service<Cx, Req> + Sync,
    S::Response: std::fmt::Debug,
    S::Error: std::fmt::Debug,
    Cx: Send + 'static,
{
    async fn call(&self, cx: &mut Cx, req: Req) -> Result<S::Response, S::Error> {
        let now = std::time::Instant::now();
        tracing::debug!("Received request {:?}", &req);
        let resp = self.0.call(cx, req).await;
        tracing::debug!("Sent response {:?}", &resp);
        tracing::info!("Request took {}ms", now.elapsed().as_millis());
        resp
    }
}

随后,我们给这个 Service 包装一层 Layer:

pub struct LogLayer;

impl<S> volo::Layer<S> for LogLayer {
    type Service = LogService<S>;

    fn layer(self, inner: S) -> Self::Service {
        LogService(inner)
    }
}

最后,我们在 client 和 server 里面加一下这个 Layer:

use volo_example::LogLayer;

// client.rs
static ref CLIENT: volo_gen::volo::example::ItemServiceClient = {
    let addr: SocketAddr = "127.0.0.1:8080".parse().unwrap();
    volo_gen::volo::example::ItemServiceClientBuilder::new("volo-example")
        .layer_outer(LogLayer)
        .address(addr)
        .build()
};

// server.rs
volo_gen::volo::example::ItemServiceServer::new(S)
    .layer_front(LogLayer)
    .run(addr)
    .await
    .unwrap();

这时候,在 info 日志级别下,我们会打印出请求的耗时;在 debug 日志级别下,我们还会打出请求和响应的详细数据。


最后修改 August 3, 2023 : chore: use codetab (#745) (015155e)