一. 攔截器
1. 工作原理
(1).流程:客戶端發送信息 → 經過客戶端攔截器 → 到達服務端攔截器 → 到達服務端方法。
如下圖:
(2).實現:都要新建1個類, 實現Interceptors接口, 但對於客戶端、服務端是 一元寫法還是流式寫法, 需要重寫的方法不同哦
A.一元寫法:客戶端重寫AsyncUnaryCall方法, 服務端重寫UnaryServerHandler方法
B.單向流寫法:客戶端重寫AsyncClientStreamingCall方法, 服務端重寫ServerStreamingServerHandler方法
C.雙向流寫法:客戶端重寫AsyncDuplexStreamingCall方法, 服務端重寫DuplexStreamingServerHandler方法
詳見下圖表格:
2. 客戶端攔截器(一元)
(1).控制台寫法
A.新建MyClientInterceptor1類,實現Interceptor接口,重寫AsyncUnaryCall方法
代碼分享:

/// <summary> /// 客戶端攔截器1 /// </summary> public class MyClientInterceptor1:Interceptor { /// <summary> /// 重寫AsyncUnaryCall,一元模式的攔截 /// </summary> /// <returns></returns> public override AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(TRequest request, ClientInterceptorContext<TRequest, TResponse> context, AsyncUnaryCallContinuation<TRequest, TResponse> continuation) { //服務於SayHello方法 if (request is HelloRequest) { HelloRequest helloRequest = request as HelloRequest; helloRequest.UserName = $"[{ DateTime.Now}]:{helloRequest.UserName}"; } //服務於CommitUserInfor方法 if (request is UserInfor) { UserInfor uInfor = request as UserInfor; uInfor.UserName = $"[{ DateTime.Now}]:{uInfor.UserName}"; uInfor.UserAge = $"[{ DateTime.Now}]:{uInfor.UserAge}"; uInfor.UserAddress = $"[{ DateTime.Now}]:{uInfor.UserAddress}"; } //添加表頭信息 var headers = context.Options.Headers; if (headers == null) { headers = new Metadata(); var options = context.Options.WithHeaders(headers); context = new ClientInterceptorContext<TRequest, TResponse>(context.Method, context.Host, options); } headers.Add("caller-user", Environment.UserName); headers.Add("caller-machine", Environment.MachineName); headers.Add("caller-os", Environment.OSVersion.ToString()); return base.AsyncUnaryCall(request, context, continuation); } }
B.給指定客戶添加過濾器: channel.Intercept(new MyClientInterceptor1());
var client1 = new Greeter.GreeterClient(intercept);
代碼分享:
using var channel = GrpcChannel.ForAddress("https://localhost:5001"); //添加攔截器 var intercept = channel.Intercept(new MyClientInterceptor1()); var client1 = new Greeter.GreeterClient(intercept); var reply = await client1.SayHelloAsync(new HelloRequest { UserName = "ypf" }); Console.WriteLine("返回的消息為: " + reply.ReplyMsg);
(2).Core Mvc寫法
A.新建MyClientInterceptor1類,實現Interceptor接口,重寫AsyncUnaryCall方法。
代碼分享:

/// <summary> /// 客戶端攔截器1 /// </summary> public class MyClientInterceptor1:Interceptor { /// <summary> /// 重寫AsyncUnaryCall,一元模式的攔截 /// </summary> /// <returns></returns> public override AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(TRequest request, ClientInterceptorContext<TRequest, TResponse> context, AsyncUnaryCallContinuation<TRequest, TResponse> continuation) { //服務於SayHello方法 if (request is HelloRequest) { HelloRequest helloRequest = request as HelloRequest; helloRequest.UserName =$"[{ DateTime.Now}]:{helloRequest.UserName}"; } //服務於CommitUserInfor方法 if (request is UserInfor) { UserInfor uInfor = request as UserInfor; uInfor.UserName = $"[{ DateTime.Now}]:{uInfor.UserName}"; uInfor.UserAge = $"[{ DateTime.Now}]:{uInfor.UserAge}"; uInfor.UserAddress = $"[{ DateTime.Now}]:{uInfor.UserAddress}"; } //添加表頭信息 var headers = context.Options.Headers; if (headers == null) { headers = new Metadata(); var options = context.Options.WithHeaders(headers); context = new ClientInterceptorContext<TRequest, TResponse>(context.Method, context.Host, options); } headers.Add("caller-user", Environment.UserName); headers.Add("caller-machine", Environment.MachineName); headers.Add("caller-os", Environment.OSVersion.ToString()); return base.AsyncUnaryCall(request, context, continuation); } }
B.給指定客戶端添加過濾器,兩種方式,詳見ConfigureService
代碼分享:
public void ConfigureServices(IServiceCollection services) { services.AddControllersWithViews(); //注冊grpc指定客戶端 + 添加攔截器模式1 services.AddGrpcClient<GreeterClient>(o => { o.Address = new Uri("https://localhost:5001"); }).AddInterceptor(() => new MyClientInterceptor1()); //注冊grpc指定客戶端 + 添加攔截器模式2 //services.AddSingleton(new MyClientInterceptor1()); //services.AddGrpcClient<GreeterClient>(o => //{ // o.Address = new Uri("https://localhost:5001"); //}).AddInterceptor<MyClientInterceptor1>(); }
3. 服務端攔截器(一元)
A.新建MyServerInterceptor1類,實現Interceptor接口,重寫UnaryServerHandler方法
代碼分享:

/// <summary> /// 服務端攔截器1 /// </summary> public class MyServerInterceptor1 : Interceptor { public override Task<TResponse> UnaryServerHandler<TRequest, TResponse>(TRequest request, ServerCallContext context, UnaryServerMethod<TRequest, TResponse> continuation) { //攔截到SayHello方法 if (request is HelloRequest) { HelloRequest helloRequest = request as HelloRequest; Console.WriteLine($"【{DateTime.Now}】我是服務端攔截下來SayHello內容:{helloRequest.UserName}"); } //攔截到CommitUserInfor方法 if (request is UserInfor) { UserInfor uInfor = request as UserInfor; Console.WriteLine($"【{DateTime.Now}】我是服務端攔截下來SayHello內容:{uInfor.UserName},{uInfor.UserAge},{uInfor.UserAddress}"); } //攔截到表頭信息 foreach (var header in context.RequestHeaders) { Console.WriteLine($"【{DateTime.Now}】我是服務端攔截表頭的內容:{header.Key}:{header.Value}"); } return base.UnaryServerHandler(request, context, continuation); } }
B.在ConfigureService中可以全局攔截 和 單個服務攔截
代碼分享:
public void ConfigureServices(IServiceCollection services) { //注冊gRPC服務 + 全局攔截 services.AddGrpc(options => { options.Interceptors.Add<MyServerInterceptor1>(); }); //注冊gRPC服務 + 單個服務攔截 //services.AddGrpc().AddServiceOptions<GreeterService>(options => { // options.Interceptors.Add<MyServerInterceptor1>(); //}); }:
4. 測試
分別把GrpcClient1和GrpcService1設置為一起啟動、GrpcClient2和GrpcService1設置為一起啟動,觀察結果。
測試1:
測試2:
二. 其它
1. 版本控制
(1). package greet.v1;
(2). endpoints.MapGrpcService<GreeterServiceV1>();
endpoints.MapGrpcService<GreeterServiceV2>();
2. 安全性
gRPC 消息使用 HTTP/2 進行發送和接收,所以建議使用傳輸層安全性 (TLS) 以保護生產 gRPC 應用中的消息,gRPC 服務應僅偵聽並響應受保護的端口,TLS 是在 Kestrel 中配置的。
詳見 GrpcService1服務端中的Program程序
public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { //TLS加密傳輸配置 webBuilder.ConfigureKestrel(serverOptions => { serverOptions.ConfigureHttpsDefaults(listenOptions => { listenOptions.SslProtocols = SslProtocols.Tls12; }); }); webBuilder.UseStartup<Startup>(); });
3. 日志
(1).說明
gRPC 服務和 gRPC 客戶端使用 .NET Core 日志記錄編寫日志。
(2).服務端日志
由於 gRPC 服務托管在 ASP.NET Core 上,因此它使用 ASP.NET Core 日志記錄系統。 在默認配置中,gRPC 只記錄很少的信息,但這可以進行配置。
在GrpcService1中Program類中進行配置,代碼如下:
public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) //配置grpc日志級別 .ConfigureLogging(logging => { logging.AddFilter("Grpc", LogLevel.Debug); }) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); });
運行結果如下:
(3).客戶端日志
A. 無DI
安裝【Microsoft.Extensions.Logging】【Microsoft.Extensions.Logging.Console】,代碼和運行效果如下
//日志 var loggerFactory = LoggerFactory.Create(logging => { logging.AddConsole(); logging.SetMinimumLevel(LogLevel.Debug); }); using var channel = GrpcChannel.ForAddress("https://localhost:5001", new GrpcChannelOptions { LoggerFactory = loggerFactory }); var client1 = new Greeter.GreeterClient(channel); var reply = await client1.SayHelloAsync(new HelloRequest { UserName = "ypf" }); Console.WriteLine("返回的消息為: " + reply.ReplyMsg);
B. 有DI
public class HomeController : Controller { public GreeterClient _client; private ILoggerFactory _loggerFactory; public HomeController(GreeterClient client, ILoggerFactory loggerFactory) { this._client = client; _loggerFactory = loggerFactory; } /// <summary> /// 客戶端調用grpc方法 /// </summary> /// <returns></returns> public async Task<IActionResult> Index() { var channel = GrpcChannel.ForAddress("https://localhost:5001", new GrpcChannelOptions { LoggerFactory = _loggerFactory }); var _client = new Greeter.GreeterClient(channel); var reply = await _client.SayHelloAsync(new HelloRequest { UserName = "ypf" }); ViewBag.msg1 = $"返回的消息為:{ reply.ReplyMsg}"; var reply2 = await _client.CommitUserInforAsync(new UserInfor() { UserName = "ypf", UserAge = "20", UserAddress = "China" }); ViewBag.msg2 = $"返回的消息為:status={reply2.Status},msg={reply2.Msg}"; return View(); } }
(4).日志指標
服務端指標:
客戶端指標:
!
- 作 者 : Yaopengfei(姚鵬飛)
- 博客地址 : http://www.cnblogs.com/yaopengfei/
- 聲 明1 : 如有錯誤,歡迎討論,請勿謾罵^_^。
- 聲 明2 : 原創博客請在轉載時保留原文鏈接或在文章開頭加上本人博客地址,否則保留追究法律責任的權利。