#middleware #tonic #grpc #interceptor #async #request-response

tonic-middleware

为 Tonic 服务提供的异步中间件和拦截器

4 个版本

0.2.1 2024 年 8 月 15 日
0.2.0 2024 年 7 月 10 日
0.1.4 2024 年 5 月 10 日
0.1.3 2024 年 3 月 24 日

317网络编程

Download history 138/week @ 2024-05-02 273/week @ 2024-05-09 55/week @ 2024-05-16 94/week @ 2024-05-23 66/week @ 2024-05-30 145/week @ 2024-06-06 93/week @ 2024-06-13 251/week @ 2024-06-20 296/week @ 2024-06-27 207/week @ 2024-07-04 523/week @ 2024-07-11 236/week @ 2024-07-18 471/week @ 2024-07-25 338/week @ 2024-08-01 883/week @ 2024-08-08 618/week @ 2024-08-15

2,382 每月下载

MIT 许可证

22KB
179

tonic-middleware

Crates.io Documentation Crates.io

目录

简介

tonic-middleware 是一个 Rust 库,它扩展了基于 tonicgRPC 服务,使您能够进行异步的检查和修改,并有可能拒绝传入的请求。它还允许通过中间件添加自定义逻辑,这可以在实际服务调用之前和之后进行。

该库提供了两个关键工具

  • 请求拦截器

    RequestInterceptor 特性旨在使您能够在服务管道中拦截和处理传入的请求。此特性特别适用于执行诸如身份验证、用额外的元数据丰富请求或基于某些标准在请求达到服务逻辑之前拒绝请求等操作。

  • 中间件

    如果您的需求超出了请求拦截,并且您需要与请求和响应进行交互,或者需要在服务调用完成之后执行操作,您应该考虑实现 Middleware

拦截器和中间件都可以应用于单个服务,也可以通过 Tonic 的层应用于所有服务。

Tonic 版本兼容性

Tonic 版本 tonic-middleware 版本 注意事项
0.11 0.1.4
0.12.x 0.2.x 破坏性变更
由 tonic 中的破坏性变更引起。
有关更多详细信息,请参阅 变更日志

用法

添加到 Cargo.toml

tonic-middleware = "0.2.1"

查看完整示例或检查集成测试

定义我们的请求拦截器和中间件

要创建请求拦截器,我们需要实现库中的RequestInterceptor特质。

注意

请使用tonic::codegen::http::{Request, Response}(这些是tonic通过http包重新导出的,即http:{Request, Response})而不是在拦截器和中间件中使用tonic::{Request, Response}

简单的请求拦截器,使用一些自定义的注入到其中以进行身份验证的AuthService。我们需要为我们的自定义(AuthInterceptor)拦截实现RequestInterceptor

use tonic::codegen::http::Request; // Use this instead of tonic::Request in Interceptor!
use tonic::codegen::http::Response; // Use this instead of tonic::Response in Interceptor!
...

#[derive(Clone)]
pub struct AuthInterceptor<A: AuthService> {
    pub auth_service: A,
}

#[async_trait]
impl<A: AuthService> RequestInterceptor for AuthInterceptor<A> {
    async fn intercept(&self, mut req: Request<BoxBody>) -> Result<Request<BoxBody>, Status> {
        match req.headers().get("authorization").map(|v| v.to_str()) {
            Some(Ok(token)) => {
                // Get user id from the token
                let user_id = self
                    .auth_service
                    .verify_token(token)
                    .await
                    .map_err(Status::unauthenticated)?;

                // Set user id in header, so it can be used in grpc services through tonic::Request::metadata()
                let user_id_header_value = HeaderValue::from_str(&user_id.to_string())
                    .map_err(|_e| Status::internal("Failed to convert user_id to header value"))?;
                req.headers_mut().insert("user_id", user_id_header_value);
                Ok(req)
            }
            _ => Err(Status::unauthenticated("Unauthenticated")),
        }
    }
}

要创建中间件,我们需要实现库中的'Middleware'特质。

度量中间件,测量请求时间并将输出打印到stdout。我们需要为我们的自定义(MetricsMiddleware)中间件实现Middleware

use tonic::codegen::http::Request; // Use this instead of tonic::Request in Middleware!
use tonic::codegen::http::Response; // Use this instead of tonic::Response in Middleware!
...

#[derive(Default, Clone)]
pub struct MetricsMiddleware;

#[async_trait]
impl<S> Middleware<S> for MetricsMiddleware
where
    S: ServiceBound,
    S::Future: Send,
{
    async fn call(
        &self,
        req: Request<Body>,
        mut service: S,
    ) -> Result<Response<BoxBody>, S::Error> {
        let start_time = Instant::now();
        // Call the service. You can also intercept request from middleware.
        let result = service.call(req).await?;

        let elapsed_time = start_time.elapsed();
        println!("Request processed in {:?}", elapsed_time);

        Ok(result)
    }
}

将请求拦截器应用于单个服务

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let addr: SocketAddr = "[::1]:50051".parse().unwrap();

    let auth_interceptor = AuthInterceptor {
        auth_service: AuthServiceImpl::default(),
    };

    // Grpc service
    let products_service = Products::default();
    let grpc_products_service = ProductServiceServer::new(products_service);

    // Grpc service
    let orders_service = Orders::default();
    let grpc_orders_service = OrderServiceServer::new(orders_service);

    println!("Grpc server listening on {}", addr);

    Server::builder()
        // No interceptor applied
        .add_service(grpc_products_service)
        // Added interceptor to single service
        .add_service(InterceptorFor::new(grpc_orders_service, auth_interceptor))
        .serve(addr)
        .await?;
 // ...
}

使用层将请求拦截器应用于所有服务

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {

    // ...
    Server::builder()
        // Interceptor can be added as a layer so all services will be intercepted
        .layer(RequestInterceptorLayer::new(auth_interceptor.clone()))
        .add_service(grpc_products_service)
        .add_service(grpc_orders_service)
        .serve(addr)
        .await?;
    // ...
}

将中间件应用于单个服务

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {

 // ...
 Server::builder()
         // Middleware can be added to individual service
         .add_service(MiddlewareFor::new(
            grpc_products_service,
            metrics_middleware,
         ))
         // No middleware applied
         .add_service(grpc_orders_service)

         .serve(addr)
         .await?;
 // ...
}

通过层将中间件应用于所有服务

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {

 // ...
 Server::builder()
         // Middleware can also be added as a layer, so it will apply to 
         // all services
         .layer(MiddlewareLayer::new(metrics_middleware))
         
         .add_service(grpc_products_service)
         .add_service(grpc_orders_service)
         .serve(addr)
         .await?;
 // ...
}

为单个服务结合拦截器和中间件

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {

    // ...
    Server::builder()
        // Middlewares and interceptors can be combined, in any order.
        // Outermost will be executed first
        .add_service(
            MiddlewareFor::new(
                InterceptorFor::new(grpc_orders_service.clone(), auth_interceptor.clone()),
                metrics_middleware.clone(),
            ))
        .add_service(grpc_products_service)    
        .await?;
    // ...
}

通过层将拦截器和中间件应用于所有服务

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {

 // ...
 Server::builder()
         // Interceptor can be added as a layer so all services will be intercepted
         .layer(RequestInterceptorLayer::new(auth_interceptor.clone()))
         // Middleware can also be added as a layer, so it will apply to all services
         .layer(MiddlewareLayer::new(metrics_middleware))
         
         .add_service(grpc_products_service)
         .add_service(grpc_orders_service)
         .await?;
 // ...
}

动机

Tonic为在Rust中开发gRPC服务提供了坚实的基础,尽管它提供了一系列功能,但通过异步拦截器和中间件扩展它需要更多努力。这就是tonic-middleware出现的地方,这个库简化了向tonic服务堆栈添加自定义异步处理。

依赖项

~5–7MB
~121K SLoC