From ba1f0c64d64692b4a1c8f366f478b6db831037cf Mon Sep 17 00:00:00 2001 From: "Pengfei(Andy) Zhang" Date: Thu, 26 Sep 2024 15:45:07 -0400 Subject: [PATCH] feat(middleware): add response extractor. --- crates/pool/src/server/remote/server.rs | 13 ++++-- crates/provider/src/traits/metrics.rs | 27 ++++++++--- crates/provider/src/traits/mod.rs | 1 + crates/rpc/src/rpc_metrics.rs | 20 +++++++-- crates/rpc/src/task.rs | 12 +++-- crates/task/src/grpc/grpc_metrics.rs | 14 +++++- crates/task/src/metrics.rs | 60 ++++++++++++++++++------- crates/types/src/task/traits.rs | 6 +++ 8 files changed, 119 insertions(+), 34 deletions(-) diff --git a/crates/pool/src/server/remote/server.rs b/crates/pool/src/server/remote/server.rs index ac452786..3f28bbf5 100644 --- a/crates/pool/src/server/remote/server.rs +++ b/crates/pool/src/server/remote/server.rs @@ -23,8 +23,11 @@ use alloy_primitives::{Address, B256}; use async_trait::async_trait; use futures_util::StreamExt; use rundler_task::{ - grpc::{grpc_metrics::HttpMethodExtractor, protos::from_bytes}, - metrics::{MetricsLayer, RequestMethodNameInfo}, + grpc::{ + grpc_metrics::{HttpMethodExtractor, HttpResponseCodeExtractor}, + protos::from_bytes, + }, + metrics::MetricsLayer, }; use rundler_types::{ chain::ChainSpec, @@ -81,7 +84,11 @@ pub(crate) async fn spawn_remote_mempool_server( .set_serving::>() .await; - let metrics_layer = MetricsLayer::::new("op_pool_service".to_string(), "http-grpc".to_string()); + let metrics_layer = + MetricsLayer::::new( + "op_pool_service".to_string(), + "http-grpc".to_string(), + ); let handle = tokio::spawn(async move { Server::builder() .layer(metrics_layer) diff --git a/crates/provider/src/traits/metrics.rs b/crates/provider/src/traits/metrics.rs index 2408e665..9cceb65c 100644 --- a/crates/provider/src/traits/metrics.rs +++ b/crates/provider/src/traits/metrics.rs @@ -11,19 +11,18 @@ // You should have received a copy of the GNU General Public License along with Rundler. // If not, see https://www.gnu.org/licenses/. +use alloy_json_rpc::{RequestPacket, ResponsePacket}; /// Method extractor -use rundler_types::task::traits::RequestExtractor; -use alloy_json_rpc::RequestPacket; +use rundler_types::task::traits::{RequestExtractor, ResponseExtractor}; +#[allow(dead_code)] #[derive(Clone, Copy)] -struct AlloyMethodExtractor; +pub struct AlloyMethodExtractor; -impl RequestExtractor for RPCMethodExtractor { +impl RequestExtractor for AlloyMethodExtractor { fn get_method_name(req: &RequestPacket) -> String { match req { - RequestPacket::Single(request) => { - request.method().to_string() - } + RequestPacket::Single(request) => request.method().to_string(), _ => { // can't extract method name for batch. "batch".to_string() @@ -31,3 +30,17 @@ impl RequestExtractor for RPCMethodExtractor { } } } + +#[allow(dead_code)] +#[derive(Clone, Copy)] +pub struct AlloyResponseCodeExtractor; + +impl ResponseExtractor for AlloyMethodExtractor { + fn get_response_code(response: &ResponsePacket) -> String { + if response.is_error() { + response.as_error().unwrap().code.to_string() + } else { + "200".to_string() + } + } +} diff --git a/crates/provider/src/traits/mod.rs b/crates/provider/src/traits/mod.rs index ff12b2ac..b1deb6b8 100644 --- a/crates/provider/src/traits/mod.rs +++ b/crates/provider/src/traits/mod.rs @@ -14,6 +14,7 @@ //! Traits for the provider module. mod error; +mod metrics; pub use error::*; mod entry_point; diff --git a/crates/rpc/src/rpc_metrics.rs b/crates/rpc/src/rpc_metrics.rs index 08542881..baf8e5c2 100644 --- a/crates/rpc/src/rpc_metrics.rs +++ b/crates/rpc/src/rpc_metrics.rs @@ -11,14 +11,28 @@ // You should have received a copy of the GNU General Public License along with Rundler. // If not, see https://www.gnu.org/licenses/. -use jsonrpsee::types::Request; -use rundler_types::task::traits::RequestExtractor; +use jsonrpsee::{types::Request, MethodResponse}; +use rundler_types::task::traits::{RequestExtractor, ResponseExtractor}; #[derive(Copy, Clone)] struct RPCMethodExtractor; impl RequestExtractor> for RPCMethodExtractor { - fn get_method_name(req: & Request<'static>) -> String { + fn get_method_name(req: &Request<'static>) -> String { req.method_name().to_string() } } + +/// http response extractor. +#[derive(Copy, Clone)] +pub struct RPCResponseCodeExtractor; + +impl ResponseExtractor for RPCResponseCodeExtractor { + fn get_response_code(response: &MethodResponse) -> String { + if response.is_error() { + response.as_error_code().unwrap().to_string() + } else { + "200".to_string() + } + } +} diff --git a/crates/rpc/src/task.rs b/crates/rpc/src/task.rs index 0ff51d59..11c98c73 100644 --- a/crates/rpc/src/task.rs +++ b/crates/rpc/src/task.rs @@ -16,7 +16,9 @@ use std::{net::SocketAddr, sync::Arc, time::Duration}; use anyhow::{bail, Context}; use async_trait::async_trait; use jsonrpsee::{ - server::{middleware::http::ProxyGetRequestLayer, RpcServiceBuilder, ServerBuilder}, types::Request, RpcModule + server::{middleware::http::ProxyGetRequestLayer, RpcServiceBuilder, ServerBuilder}, + types::Request, + RpcModule, }; use rundler_provider::{EntryPointProvider, Provider}; use rundler_sim::{ @@ -42,10 +44,9 @@ use crate::{ EthApiSettings, UserOperationEventProviderV0_6, UserOperationEventProviderV0_7, }, health::{HealthChecker, SystemApiServer}, - rpc_metrics, + rpc_metrics::{RPCMethodExtractor, RPCResponseCodeExtractor}, rundler::{RundlerApi, RundlerApiServer, Settings as RundlerApiSettings}, types::ApiNamespace, - rpc_metrics::RPCMethodExtractor, }; /// RPC server arguments. @@ -188,7 +189,10 @@ where .timeout(self.args.rpc_timeout); let rpc_metric_middleware = - MetricsLayer::::new("rundler-eth-service".to_string(), "rpc".to_string()); + MetricsLayer::, RPCResponseCodeExtractor>::new( + "rundler-eth-service".to_string(), + "rpc".to_string(), + ); let server = ServerBuilder::default() .set_http_middleware(http_middleware) diff --git a/crates/task/src/grpc/grpc_metrics.rs b/crates/task/src/grpc/grpc_metrics.rs index 762786fe..47acea5e 100644 --- a/crates/task/src/grpc/grpc_metrics.rs +++ b/crates/task/src/grpc/grpc_metrics.rs @@ -11,10 +11,10 @@ // You should have received a copy of the GNU General Public License along with Rundler. // If not, see https://www.gnu.org/licenses/. -use rundler_types::task::traits::RequestExtractor; +use rundler_types::task::traits::{RequestExtractor, ResponseExtractor}; use tonic::codegen::http; -/// http request method extractor. +/// http request method extractor. #[derive(Copy, Clone)] struct HttpMethodExtractor; @@ -24,3 +24,13 @@ impl RequestExtractor> for HttpMethodExtractor { method_name.to_string() } } + +/// http response extractor. +#[derive(Copy, Clone)] +pub struct HttpResponseCodeExtractor; + +impl ResponseExtractor> for HttpResponseCodeExtractor { + fn get_response_code(response: &http::Response) -> String { + response.status().to_string() + } +} diff --git a/crates/task/src/metrics.rs b/crates/task/src/metrics.rs index d492745e..d13655c3 100644 --- a/crates/task/src/metrics.rs +++ b/crates/task/src/metrics.rs @@ -20,19 +20,20 @@ use std::{ }; use futures::{future::BoxFuture, FutureExt}; -use rundler_types::task::traits::RequestExtractor; +use rundler_types::task::traits::{RequestExtractor, ResponseExtractor}; use tower::{Layer, Service}; /// tower network layer: https://github.com/tower-rs/tower/blob/master/guides/building-a-middleware-from-scratch.md #[derive(Debug, Clone)] -pub struct MetricsLayer { +pub struct MetricsLayer { service_name: String, protocal: String, _request_extractor_: PhantomData, _request_type_: PhantomData, + _response_extractor_: PhantomData, } -impl MetricsLayer +impl MetricsLayer where T: RequestExtractor, { @@ -43,30 +44,36 @@ where protocal, _request_extractor_: PhantomData, _request_type_: PhantomData, + _response_extractor_: PhantomData, } } } -impl Layer for MetricsLayer +impl Layer for MetricsLayer where T: RequestExtractor, { - type Service = MetricsMiddleware; + type Service = MetricsMiddleware; fn layer(&self, service: S) -> Self::Service { - MetricsMiddleware::::new(service, self.service_name.clone(), self.protocal.clone()) + MetricsMiddleware::::new( + service, + self.service_name.clone(), + self.protocal.clone(), + ) } } /// Middleware implementation. -pub struct MetricsMiddleware { +pub struct MetricsMiddleware { inner: S, service_name: String, protocal: String, _request_extractor_: PhantomData, _request_type_: PhantomData, + _response_extractor_: PhantomData, } -impl MetricsMiddleware +impl MetricsMiddleware where T: RequestExtractor, { @@ -78,16 +85,18 @@ where protocal: protocal, _request_extractor_: PhantomData, _request_type_: PhantomData, + _response_extractor_: PhantomData, } } } -impl Service for MetricsMiddleware +impl Service for MetricsMiddleware where S: Service + Send + Sync + Clone + 'static, S::Future: Send + Sync + 'static, T: RequestExtractor + 'static, Request: Send + Sync + 'static, + RE: ResponseExtractor + Send + Sync + 'static, { type Response = S::Response; type Error = S::Error; @@ -128,12 +137,24 @@ where service_name.as_str(), protocal.as_str(), ); - if rsp.is_err() { - MethodMetrics::increment_error_count( - method_name.as_str(), - service_name.as_str(), - protocal.as_str(), - ); + + match &rsp { + Ok(response) => { + let response_code = RE::get_response_code(response); + MethodMetrics::increment_response_code( + method_name.as_str(), + service_name.as_str(), + protocal.as_str(), + response_code.as_str(), + ); + } + Err(_) => { + MethodMetrics::increment_error_count( + method_name.as_str(), + service_name.as_str(), + protocal.as_str(), + ); + } } rsp } @@ -161,6 +182,15 @@ impl MethodMetrics { metrics::counter!("open_requests", "method_name" => method_name.to_string(), "service_name" => service_name.to_string(), "protocal" => protocal.to_string()).increment(1) } + fn increment_response_code( + method_name: &str, + service_name: &str, + protocal: &str, + response_code: &str, + ) { + metrics::counter!("response_stats", "method_name" => method_name.to_string(), "service_name" => service_name.to_string(), "protocal" => protocal.to_string(), "response_code" => response_code.to_string()).increment(1) + } + fn record_request_latency( method_name: &str, service_name: &str, diff --git a/crates/types/src/task/traits.rs b/crates/types/src/task/traits.rs index 95ba2a63..8dd6a78b 100644 --- a/crates/types/src/task/traits.rs +++ b/crates/types/src/task/traits.rs @@ -17,3 +17,9 @@ pub trait RequestExtractor: Copy + Sync + Send { /// Get method name. fn get_method_name(request: &R) -> String; } + +/// Trait to extract response code. +pub trait ResponseExtractor: Copy + Sync + Send { + /// Get response code. + fn get_response_code(response: &R) -> String; +}