grpc实现Aop

创建项目

  1. 服务端:微软官方自带的ASP.NET.Core.gRPC服务项目。
  2. 客户端:ASP.NET.Core.WebApi项目。
  3. 公共类库:主要为AOP自定义拦截器类。

依赖包导入

客户端:Grpc.AspNetCore、Grpc.Core.Api、Grpc.Net.ClientFactory、Grpc.Tools。

公共类库:Grpc.Core.Api

公共类库项目配置

创建GrpcAopInterceptor类文件并配置如下内容。

 /// <summary>
 /// 自定义拦截器
 /// </summary>
 public class GrpcAopInterceptor : Interceptor
 {
     #region 客户端Aop
     /// <summary>
     /// 客户端发送单个请求,阻塞等待单个响应,用于同步RPC调用
     /// </summary>
     /// <typeparam name="TRequest"></typeparam>
     /// <typeparam name="TResponse"></typeparam>
     /// <param name="request"></param>
     /// <param name="context"></param>
     /// <param name="continuation"></param>
     /// <returns></returns>
     public override TResponse BlockingUnaryCall<TRequest, TResponse>(TRequest request, ClientInterceptorContext<TRequest, TResponse> context, BlockingUnaryCallContinuation<TRequest, TResponse> continuation)
     {
         try
         {
             // 记录请求信息
             LogRequest(context.Method.FullName, request);
             // 调用服务
             var response = continuation(request, context);
             // 记录响应信息
             LogResponse(context.Method.FullName, response);
             return response;
         }
         catch (RpcException ex)
         {
             // 处理异常情况
             LogError(context.Method.FullName, ex);
             throw;
         }

     }

     /// <summary>
     /// 客户端发送单个请求,非阻塞地等待单个响应,用于异步RPC调用
     /// </summary>
     /// <typeparam name="TRequest"></typeparam>
     /// <typeparam name="TResponse"></typeparam>
     /// <param name="request"></param>
     /// <param name="context"></param>
     /// <param name="continuation"></param>
     /// <returns></returns>
     public override AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(TRequest request, ClientInterceptorContext<TRequest, TResponse> context, AsyncUnaryCallContinuation<TRequest, TResponse> continuation)
     {
         try
         {
             // 记录请求信息
             LogRequest(context.Method.FullName, request);
             // 调用服务
             var call = continuation(request, context);
             // 记录响应信息
             var responseAsync = HandleResponseAsync(call, context.Method.FullName);

             return new AsyncUnaryCall<TResponse>(
                  responseAsync,
                  call.ResponseHeadersAsync,
                  call.GetStatus,
                  call.GetTrailers,
                  call.Dispose);
         }
         catch (RpcException ex)
         {
             // 处理异常情况
             LogError(context.Method.FullName, ex);
             throw;
         }
     }

     /// <summary>
     /// 客户端发送多个请求(流),接收单个响应,用于客户端流式 RPC
     /// </summary>
     /// <typeparam name="TRequest"></typeparam>
     /// <typeparam name="TResponse"></typeparam>
     /// <param name="context"></param>
     /// <param name="continuation"></param>
     /// <returns></returns>
     public override AsyncClientStreamingCall<TRequest, TResponse> AsyncClientStreamingCall<TRequest, TResponse>(ClientInterceptorContext<TRequest, TResponse> context, AsyncClientStreamingCallContinuation<TRequest, TResponse> continuation)
     {
         try
         {
             // 记录调用开始信息
             LogRequestStart(context.Method.FullName);

             // 调用实际的服务
             var call = continuation(context);

             // 处理请求流
             var requestStream = new LoggingAsyncStreamWriter<TRequest>(call.RequestStream, context.Method.FullName);

             // 处理响应
             var responseAsync = HandleResponseAsync<TRequest, TResponse>(call, context.Method.FullName);

             return new AsyncClientStreamingCall<TRequest, TResponse>(
                 requestStream,
                 responseAsync,
                 call.ResponseHeadersAsync,
                 call.GetStatus,
                 call.GetTrailers,
                 call.Dispose);
         }
         catch (RpcException ex)
         {
             // 处理异常情况
             LogError(context.Method.FullName, ex);
             throw;
         }
     }
     #endregion

     #region 服务端Aop
     /// <summary>
     /// 客户端发送单个请求,接收多个响应(流),用于服务器流式 RPC
     /// </summary>
     /// <typeparam name="TRequest"></typeparam>
     /// <typeparam name="TResponse"></typeparam>
     /// <param name="request"></param>
     /// <param name="context"></param>
     /// <param name="continuation"></param>
     /// <returns></returns>
     public override AsyncServerStreamingCall<TResponse> AsyncServerStreamingCall<TRequest, TResponse>(TRequest request, ClientInterceptorContext<TRequest, TResponse> context, AsyncServerStreamingCallContinuation<TRequest, TResponse> continuation)
     {
         try
         {
             // 记录请求信息
             LogRequest(context.Method.FullName, request);

             // 调用实际的服务
             var call = continuation(request, context);

             // 处理响应流
             var responseStream = new LoggingAsyncStreamReader<TResponse>(call.ResponseStream, context.Method.FullName);

             return new AsyncServerStreamingCall<TResponse>(
                 responseStream,
                 call.ResponseHeadersAsync,
                 call.GetStatus,
                 call.GetTrailers,
                 call.Dispose);
         }
         catch (RpcException ex)
         {
             // 处理异常情况
             LogError(context.Method.FullName, ex);
             throw;
         }
     }
     #endregion

     #region 双向流式Aop
     /// <summary>
     /// 客户端和服务器都可以发送多个消息,用于双向流式 RPC
     /// </summary>
     /// <typeparam name="TRequest"></typeparam>
     /// <typeparam name="TResponse"></typeparam>
     /// <param name="context"></param>
     /// <param name="continuation"></param>
     /// <returns></returns>
     public override AsyncDuplexStreamingCall<TRequest, TResponse> AsyncDuplexStreamingCall<TRequest, TResponse>(ClientInterceptorContext<TRequest, TResponse> context, AsyncDuplexStreamingCallContinuation<TRequest, TResponse> continuation)
     {
         try
         {
             // 记录调用开始信息
             LogRequestStart(context.Method.FullName);

             // 调用实际的服务
             var call = continuation(context);

             // 处理请求流
             var requestStream = new LoggingAsyncStreamWriter<TRequest>(call.RequestStream, context.Method.FullName);

             // 处理响应流
             var responseStream = new LoggingAsyncStreamReader<TResponse>(call.ResponseStream, context.Method.FullName);

             return new AsyncDuplexStreamingCall<TRequest, TResponse>(
                 requestStream,
                 responseStream,
                 call.ResponseHeadersAsync,
                 call.GetStatus,
                 call.GetTrailers,
                 call.Dispose);
         }
         catch (RpcException ex)
         {
             // 处理异常情况
             LogError(context.Method.FullName, ex);
             throw;
         }
     }
     #endregion

     private void AsyncClientStreamingLogAop<TRequest, TResponse>(Method<TRequest, TResponse> method)
         where TRequest : class
         where TResponse : class
     {
         Console.WriteLine("************************客户端流式RPC-Aop 开始************************");
         Console.WriteLine($"{method.Name}---{method.FullName}--{method.ServiceName}");
         Console.WriteLine($"Type: {method.Type}. Request: {typeof(TRequest)}. Response: {typeof(TResponse)}");
         Console.WriteLine("************************客户端流式RPC-Aop 结束************************");
     }

     /// <summary>
     /// 记录请求日志
     /// </summary>
     /// <typeparam name="TRequest"></typeparam>
     /// <param name="methodName"></param>
     /// <param name="request"></param>
     private void LogRequest<TRequest>(string methodName, TRequest request)
     {
         Console.WriteLine("************************记录请求日志开始************************");
         Console.WriteLine($"Sending request to {methodName}: {request}");
         Console.WriteLine("************************记录请求日志结束************************");
     }

     /// <summary>
     /// 记录响应日志
     /// </summary>
     /// <typeparam name="TResponse"></typeparam>
     /// <param name="methodName"></param>
     /// <param name="response"></param>
     private void LogResponse<TResponse>(string methodName, TResponse response)
     {
         Console.WriteLine("************************记录响应日志开始************************");
         Console.WriteLine($"Received response from {methodName}: {response}");
         Console.WriteLine("************************记录响应日志结束************************");
     }

     // 记录请求开始日志
     private void LogRequestStart(string methodName)
     {
         Console.WriteLine("************************记录请求日志开始************************");
         Console.WriteLine($"Starting client streaming call to {methodName}");
         Console.WriteLine("************************记录请求日志结束************************");
     }



     /// <summary>
     /// 记录错误日志
     /// </summary>
     /// <param name="methodName"></param>
     /// <param name="ex"></param>
     private void LogError(string methodName, RpcException ex)
     {
         Console.WriteLine($"Error occurred while calling {methodName}: {ex.Status}");
     }

     private async Task<TResponse> HandleResponseAsync<TResponse>(AsyncUnaryCall<TResponse> call, string methodName)
     {
         try
         {
             var response = await call.ResponseAsync;
             LogResponse(methodName, response);
             return response;
         }
         catch (RpcException ex)
         {
             LogError(methodName, ex);
             throw;
         }
     }
     private async Task<TResponse> HandleResponseAsync<TRequest, TResponse>(AsyncClientStreamingCall<TRequest, TResponse> call, string methodName)
     {
         try
         {
             var response = await call.ResponseAsync;
             LogResponse(methodName, response);
             return response;
         }
         catch (RpcException ex)
         {
             LogError(methodName, ex);
             throw;
         }
     }

     // 自定义的请求流写入器,用于记录请求日志
     internal class LoggingAsyncStreamWriter<T> : IClientStreamWriter<T>
     {
         private readonly IClientStreamWriter<T> _inner;
         private readonly string _methodName;

         public LoggingAsyncStreamWriter(IClientStreamWriter<T> inner, string methodName)
         {
             _inner = inner;
             _methodName = methodName;
         }

         public WriteOptions WriteOptions
         {
             get => _inner.WriteOptions;
             set => _inner.WriteOptions = value;
         }

         public async Task CompleteAsync()
         {
             Console.WriteLine($"Completing request stream to {_methodName}");
             await _inner.CompleteAsync();
         }

         public async Task WriteAsync(T message)
         {
             Console.WriteLine($"Sending request to {_methodName}: {message}");
             await _inner.WriteAsync(message);
         }
     }

     // 自定义的响应流读取器,用于记录响应日志
     internal class LoggingAsyncStreamReader<T> : IAsyncStreamReader<T>
     {
         private readonly IAsyncStreamReader<T> _inner;
         private readonly string _methodName;

         public LoggingAsyncStreamReader(IAsyncStreamReader<T> inner, string methodName)
         {
             _inner = inner;
             _methodName = methodName;
         }

         public T Current => _inner.Current;

         public async Task<bool> MoveNext(System.Threading.CancellationToken cancellationToken)
         {
             var result = await _inner.MoveNext(cancellationToken);
             if (result)
             {
                 Console.WriteLine($"Received response from {_methodName}: {_inner.Current}");
             }
             return result;
         }
     }
 }

服务端项目配置

  1. 创建demo.proto文件。
  2. 配置如下内容,设置类型为Server.Only
syntax = "proto3";

option csharp_namespace = "GrpcService_Server";

package demo;

service DemoGreeter {
  // 简单RPC
  rpc SayHelloClientNormal (HelloNormalRequest) returns (HelloNormalReply);
  // 客户端流式 RPC 方法
  rpc SayHelloClientStream (stream SayHelloClientRequest) returns (SayHelloClientHelloReply);
  // 服务器流式 RPC 方法
  rpc SayHelloServerStream (SayHelloServerRequest) returns (stream SayHelloServerReply);
  // 双向流式 RPC 方法
  rpc SayHelloBidirectionalStream (stream SayHelloBidirectionalRequest) returns (stream SayHelloBidirectionalReply);

}

message HelloNormalRequest {
  string name = 1;
}

message HelloNormalReply {
  string message = 1;
}

message SayHelloClientRequest {
  string name = 1;
}

message SayHelloClientHelloReply {
  string message = 1;
}

message SayHelloServerRequest {
  string name = 1;
}

message SayHelloServerReply {
  string message = 1;
}

message SayHelloBidirectionalRequest {
  string name = 1;
}

message SayHelloBidirectionalReply {
  string message = 1;
}

  1. 添加服务端服务实现代码
 public class DemoService : GrpcService_Server.DemoGreeter.DemoGreeterBase
 {
     public override Task<HelloNormalReply> SayHelloClientNormal(HelloNormalRequest request, ServerCallContext context)
     {
         return Task.FromResult(new HelloNormalReply
         {
             Message = "Hello " + request.Name
         });
     }

      public override async Task<SayHelloClientHelloReply> SayHelloClientStream(IAsyncStreamReader<SayHelloClientRequest> requestStream, ServerCallContext context)
 {
     var names = new List<string>();
     while (await requestStream.MoveNext())
     {
         names.Add(requestStream.Current.Name);
     }
     return new SayHelloClientHelloReply
     {
         //把客户端传过来的姓名拼接起来输出
         Message = "Hello, " + string.Join(", ", names)
     };
 }

 public override async Task SayHelloServerStream(SayHelloServerRequest request, IServerStreamWriter<SayHelloServerReply> responseStream, ServerCallContext context)
 {
     for (int i = 0; i < 3; i++)
     {
         await responseStream.WriteAsync(new SayHelloServerReply
         {
             Message = $"Hello {request.Name} - {i}"
         });
         await Task.Delay(1000);
     }
 }

 public override async Task SayHelloBidirectionalStream(IAsyncStreamReader<SayHelloBidirectionalRequest> requestStream, IServerStreamWriter<SayHelloBidirectionalReply> responseStream, ServerCallContext context)
 {
     await foreach (var request in requestStream.ReadAllAsync())
     {
         await responseStream.WriteAsync(new SayHelloBidirectionalReply
         {
             Message = $"Hello {request.Name}"
         });
     }
 }
     
 }
  1. IOC注入grpc服务
  builder.Services.AddGrpc();
  1. 启用AOP拦截

调整上述代码,改成如下形式。

  builder.Services.AddGrpc(options =>
  {
      //启用Aop拦截
      options.Interceptors.Add<GrpcAopInterceptor>();
  });
  1. 添加服务到Http请求管道
app.MapGrpcService<DemoService>();

客户端项目配置

  1. 创建Protos文件夹并将服务端的demo.proto导入进来,同时设置类型为Client.Only
  2. 添加控制器gRPCController,并配置如下内容
 /// <summary>
 /// grpc controller
 /// </summary>
 [Route("api/[controller]")]
 [ApiController]
 public class gRPCController : ControllerBase
 {
     private readonly DemoGreeter.DemoGreeterClient _greeterClient;

     public gRPCController(DemoGreeter.DemoGreeterClient greeterClient)
     {
         this._greeterClient = greeterClient;
     }

     /// <summary>
     /// 简单RPC-同步表
     /// </summary>
     /// <returns></returns>
     [HttpGet("Normal", Name = "Normal")]
     public IActionResult SayHello()
     {
         var result = _greeterClient.SayHelloClientNormal(new HelloNormalRequest() { Name = "Jack" });
         Console.WriteLine($"普通调用, SayHello获取服务端返回的数据:{result.Message}");
         return Ok(result.Message);
     }

     /// <summary>
     /// 简单RPC-异步
     /// </summary>
     /// <returns></returns>
     [HttpGet("NormalAsync", Name = "NormalAsync")]
     public async Task<IActionResult> SayHelloAsync()
     {
         var result = await _greeterClient.SayHelloClientNormalAsync(new HelloNormalRequest() { Name = "Jack" });
         Console.WriteLine($"普通调用, SayHelloAsync获取服务端返回的数据:{result.Message}");
         return Ok(result.Message);
     }

     /// <summary>
     /// 客户端流RPC
     /// </summary>
     /// <returns></returns>
     [HttpGet("ClientStream", Name = "ClientStream")]
     public async Task<IActionResult> SayHelloClientStreamAsync()
     {
         // 发起客户端流式调用
         var call = _greeterClient.SayHelloClientStream();
         var requestStream = call.RequestStream;

         // 模拟要发送的姓名列表
         var names = new List<string> { "Alice", "Bob", "Charlie" };

         // 向服务端发送请求
         foreach (var name in names)
         {
             var request = new SayHelloClientRequest { Name = name };
             await requestStream.WriteAsync(request);
         }

         // 告知服务端请求发送完毕
         await requestStream.CompleteAsync();

         // 等待服务端响应
         var response = await call.ResponseAsync;

         Console.WriteLine($"客户端RPC流调用, SayHelloClientStreamAsync获取服务端返回的数据:{response.Message}");

         return Ok(response.Message);
     }

     /// <summary>
     /// 服务端流RPC
     /// </summary>
     /// <returns></returns>
     [HttpGet("ServerStream", Name = "ServerStream")]
     public async Task<IActionResult> SayHelloServerStreamAsync()
     {
         // 创建请求消息
         var request = new SayHelloServerRequest
         {
             Name = "World" // 可根据需求修改姓名
         };

         // 发起服务端流式调用
         var call = _greeterClient.SayHelloServerStream(request);

         // 存储服务端返回的所有消息
         var responses = new List<string>();

         // 读取服务端的流式响应
         await foreach (var response in call.ResponseStream.ReadAllAsync())
         {
             responses.Add(response.Message);
         }

         Console.WriteLine($"服务端RPC流调用, SayHelloServerStreamAsync获取服务端返回的数据:{string.Join(",", responses)}");

         // 返回响应给客户端
         return Ok(responses);
     }

     /// <summary>
     /// 双向流RPC
     /// </summary>
     /// <returns></returns>
     [HttpGet("DoubleStream", Name = "DoubleStream")]
     public async Task<IActionResult> SayHelloDoubleStreamAsync()
     {
         // 发起双向流式调用
         var call = _greeterClient.SayHelloBidirectionalStream();

         // 存储服务端返回的所有消息
         var responses = new List<string>();

         // 模拟要发送的姓名列表
         var names = new List<string> { "Alice", "Bob", "Charlie" };

         // 并行处理请求发送和响应接收
         var sendTask = SendRequestsAsync(call.RequestStream, names);
         var receiveTask = ReceiveResponsesAsync(call.ResponseStream, responses);

         // 等待发送和接收任务完成
         await Task.WhenAll(sendTask, receiveTask);

         Console.WriteLine($"双向RPC流调用, SayHelloDoubleStreamAsync获取服务端返回的数据:{string.Join(",", responses)}");

         // 返回响应给客户端
         return Ok(responses);
     }


     private async Task SendRequestsAsync(IClientStreamWriter<SayHelloBidirectionalRequest> requestStream, List<string> names)
     {
         foreach (var name in names)
         {
             var request = new SayHelloBidirectionalRequest { Name = name };
             await requestStream.WriteAsync(request);
             await Task.Delay(100); // 可根据实际情况调整延迟
         }
         await requestStream.CompleteAsync();
     }
     private async Task ReceiveResponsesAsync(IAsyncStreamReader<SayHelloBidirectionalReply> responseStream, List<string> responses)
     {
         await foreach (var response in responseStream.ReadAllAsync())
         {
             responses.Add(response.Message);
         }
     }

 }
  1. IOC注入grpc服务
  builder.Services.AddGrpc();
  1. 启用AOP拦截

调整上述代码,改成如下形式。

注意:客户端注册AOP拦截器有几种方式

  • 方式一:opt.Interceptors.Add(new GrpcAopInterceptor())
  • 方式二:.AddInterceptor<GrpcAopInterceptor>()
  • 方式三:opt.InterceptorRegistrations.Add(new InterceptorRegistration(InterceptorScope.Client, provider => new GrpcAopInterceptor()))
   //IOC注册gRPC客户端
   builder.Services.AddGrpcClient<DemoGreeter.DemoGreeterClient>(opt =>
   {
       //配置连接地址
       opt.Address = new Uri("http://localhost:5200");
       //opt.Interceptors.Add(new GrpcAopInterceptor());
       //注册拦截器到 GrpcClient
       opt.InterceptorRegistrations.Add(new InterceptorRegistration(InterceptorScope.Client, provider => new GrpcAopInterceptor()));
   });//.AddInterceptor<GrpcAopInterceptor>();

运行项目

  1. 简单RPC-同步调用

  1. 简单RPC-异步调用

  1. 客户端流RPC调用

  1. 服务端流RPC调用

  1. 双向流RPC调用

posted @ 2025-03-29 00:50  相遇就是有缘  阅读(13)  评论(0)    收藏  举报