zoukankan      html  css  js  c++  java
  • 第三十五节: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 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。
     
  • 相关阅读:
    【转】cocos2d-x使用第三方的TTF字体库
    CCControlSlider和CCControlStepper用法
    Cocos2d-x中获取设备语言的方法
    ccrendertexture
    【转】如何使用KeyChain保存和获取UDID
    【luogu P4777】【模板】扩展中国剩余定理(EXCRT)(数论)
    【luogu P1495】【模板】中国剩余定理(CRT)/曹冲养猪(数论)
    【luogu P3980】Volunteer / 志愿者招募(网络流)
    凡喵识图 / Image Recognition(鸽笼原理)(模拟)
    回文树(并查集)(倍增)(LCA)(ST 表)
  • 原文地址:https://www.cnblogs.com/yaopengfei/p/13379197.html
Copyright © 2011-2022 走看看