如何在Tonic服务器的Tower Layer中监控请求响应字节大小?
Tonic服务器添加端点收发字节数监控的实现问题
我有一个Tonic服务器,已经实现了用于监控请求延迟的Tower Layer(代码摘要如下)。现在需要给每个端点添加接收/发送字节数的监控。
RequestBody最终会实现prost::Message,因此具备encoded_len方法,但我不确定该方法是否需要消费消息才能获取长度。我考虑过几种方案,但都有问题:
- 理论上可以修改
tonic::Request::into_inner()来记录长度,但这并非最优解; - 也可以在客户端添加日志,但我希望避免这种方式;
- 我期望在同一个Layer中同时记录请求和响应大小,但不知道如何借助现有的延迟监控工具实现。
现有延迟监控Layer代码
impl<T, RequestBody, ResponseBody: 'static> Service<tonic::codegen::http::Request<RequestBody>> for EndpointTimer<T> where T: Service< tonic::codegen::http::Request<RequestBody>, Response = tonic::codegen::http::Response<ResponseBody>, > + Send, T::Future: Send + 'static, T::Error: Into<Box<dyn Error + Send + Sync>> + 'static, T::Response: 'static, { type Response = T::Response; type Error = Box<dyn Error + Send + Sync>; type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>; fn poll_ready(...) { ... } fn call(&mut self, req: tonic::codegen::http::Request<RequestBody>) -> Self::Future { let start = Instant::now(); let future = self.inner.call(req); let timed_future = async move { let res = future.await; // ... 计时并记录延迟时长 res.map_err(|err| err.into()) }; Box::pin(timed_future) } } // 用于包装服务的EndpointTimer层 #[derive(Clone)] pub struct EndpointTimerLayer(); impl EndpointTimerLayer { pub fn new() -> Self { EndpointTimerLayer() } } impl<S> Layer<S> for EndpointTimerLayer { type Service = EndpointTimer<S>; fn layer(&self, service: S) -> EndpointTimer<S> { EndpointTimer::new(service) } }
编辑补充
我进一步研究后认为这可能无法实现。生成的服务器代码(位于target目录)如下:
impl<T, B> tonic::codegen::Service<http::Request<B>> for MyServer<T> where [...] { [...] fn call(&mut self, req: http::Request<B>) -> Self::Future { [...] match req.uri().path() { "/my_service.MyServiceProvider/MyEndpoint" => { [...] let fut = async move { [...] let res = grpc.unary(method, req).await; Ok(res) }; Box::pin(fut) }
我曾考虑重载http::Request的into_parts方法,将请求大小发送到Prometheus,因为此时请求体是tonic::Request,它具备into_inner方法,可返回拥有encoded_len()的prost::Message。但因http::Request::into_parts和tonic::Request::into_inner都不是trait实现,我认为这不可行。
内容的提问来源于stack exchange,提问作者gmoss
相关产品推荐
相关产品推荐

