grpc实现Aop
创建项目
- 服务端:微软官方自带的
ASP.NET.Core.gRPC服务项目。 - 客户端:
ASP.NET.Core.WebApi项目。 - 公共类库:主要为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;
}
}
}
服务端项目配置
- 创建demo.proto文件。
- 配置如下内容,设置类型为
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;
}

- 添加服务端服务实现代码
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}"
});
}
}
}
- IOC注入grpc服务
builder.Services.AddGrpc();
- 启用AOP拦截
调整上述代码,改成如下形式。
builder.Services.AddGrpc(options =>
{
//启用Aop拦截
options.Interceptors.Add<GrpcAopInterceptor>();
});
- 添加服务到Http请求管道
app.MapGrpcService<DemoService>();
客户端项目配置
- 创建Protos文件夹并将服务端的demo.proto导入进来,同时设置类型为
Client.Only。 - 添加控制器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);
}
}
}
- IOC注入grpc服务
builder.Services.AddGrpc();
- 启用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>();
运行项目
- 简单RPC-同步调用

- 简单RPC-异步调用

- 客户端流RPC调用

- 服务端流RPC调用

- 双向流RPC调用

人生如逆旅
我亦是行人

浙公网安备 33010602011771号