第三十五節:gRPC攔截器、版本控制、安全性、日志集成


一. 攔截器

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);
        }
    }
View Code

 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);
        }
    }
View Code

 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);
        }
    }
View Code

 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 : 原創博客請在轉載時保留原文鏈接或在文章開頭加上本人博客地址,否則保留追究法律責任的權利。
 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM