• gRPC Client Middleware.


      中间件想必大家不陌生,今天给大家介绍如何实现中间件以及实现gRPC的客户端中间件。

    什么是中间件?

       https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/middleware/index?view=aspnetcore-2.1&tabs=aspnetcore2x

    中间件管道

     先定义管道Pipeline

    /// <summary>
        /// Built pipeline for gRPC
        /// </summary>
        public class Pipeline
        {
            private PipelineDelagate processChain;
    
            internal Pipeline(PipelineDelagate middlewareChain)
            {
                processChain = middlewareChain;
            }
    
            internal Task RunPipeline(MiddlewareContext context)
            {
                return processChain(context);
            }
        }
    

    然后实现PipelineBuilder

    /// <summary>
        /// PipelineBuilder
        /// </summary>
        public class PipelineBuilder
        {
            private List<Func<PipelineDelagate, PipelineDelagate>> middlewares = new List<Func<PipelineDelagate, PipelineDelagate>>();
    
            /// <summary>
            /// Add a middleware
            /// </summary>
            public PipelineBuilder Use(Func<PipelineDelagate, PipelineDelagate> middleware)
            {
                middlewares.Add(middleware);
                return this;
            }
    
            /// <summary>
            /// Use
            /// </summary>
            public PipelineBuilder Use<T>(params object[] args)
            {
                middlewares.Add(d => WrapClass<T>(d, args));
                return this;
            }
    
            /// <summary>
            /// UseWhen
            /// </summary>
            public PipelineBuilder UseWhen<T>(Func<MiddlewareContext, bool> condition, params object[] args)
            {
                middlewares.Add(d =>
                {
                    return async ctx => { if (condition(ctx)) { await WrapClass<T>(d, args)(ctx); } };
                });
                return this;
            }
    
            /// <summary>
            /// Use
            /// </summary>
            public PipelineBuilder Use(Func<MiddlewareContext, PipelineDelagate, Task> middleware)
            {
                middlewares.Add(d =>
                {
                    return ctx => { return middleware(ctx, d); };
                });
                return this;
            }
    
            private PipelineDelagate WrapClass<T>(PipelineDelagate next, params object[] args)
            {
                var ctorArgs = new object[args.Length + 1];
                ctorArgs[0] = next;
                Array.Copy(args, 0, ctorArgs, 1, args.Length);
                var type = typeof(T);
                var instance = Activator.CreateInstance(type, ctorArgs);
                MethodInfo method = type.GetMethod("Invoke");
                return (PipelineDelagate)method.CreateDelegate(typeof(PipelineDelagate), instance);
            }
    
            /// <summary>
            /// Build
            /// </summary>
            public Pipeline Build()
            {
                PipelineDelagate pipeline = ExecuteMainHandler;
                middlewares.Reverse();
                foreach (var middleware in middlewares)
                {
                    pipeline = middleware(pipeline);
                }
                return new Pipeline(pipeline);
            }
    
            internal static Task ExecuteMainHandler(MiddlewareContext context)
            {
                return context.HandlerExecutor();
            }
        }
    MiddlewareContext 和 委托定义
    /// <summary>
        /// MiddlewareContext
        /// </summary>
        public class MiddlewareContext
        {
            public IMethod Method { get; set; }
            
            public object Request { get; set; }
           
            public object Response { get; set; }
    
            public string Host { get; set; }
    
            public CallOptions Options { get; set; }
    
            internal Func<Task> HandlerExecutor { get; set; }
        }
    
    
        public delegate Task PipelineDelagate(MiddlewareContext context);
    

    到这里管道建设完成,那个如何在gRPC中使用呢?

    首先实现自己的客户端拦截器MiddlewareCallInvoker

     public sealed class MiddlewareCallInvoker : DefaultCallInvoker
        {
            private readonly Channel grpcChannel;
    
            private Pipeline MiddlewarePipeline { get; set; }
    
            public MiddlewareCallInvoker(Channel channel) : base(channel)
            {
                this.grpcChannel = channel;
            }
    
            public MiddlewareCallInvoker(Channel channel, Pipeline pipeline) : this(channel)
            {
                this.MiddlewarePipeline = pipeline;
            }
    
            private TResponse Call<TResponse>(Func<MiddlewareContext, TResponse> call, MiddlewareContext context)
            {
             
                TResponse response = default(TResponse);
                if (MiddlewarePipeline != null)
                {
                    context.HandlerExecutor = async () =>
                    {
                        response = await Task.FromResult(call(context));
                        context.Response = response;
                    };
                    MiddlewarePipeline.RunPipeline(context).ConfigureAwait(false);
                }
                else
                {
                    response = call(context);
                }
                return response;
           
            }
    
            public override TResponse BlockingUnaryCall<TRequest, TResponse>(Method<TRequest, TResponse> method,
                string host, CallOptions options, TRequest request)
            {
                return Call((context) => base.BlockingUnaryCall((Method<TRequest, TResponse>)context.Method, context.Host, context.Options, (TRequest)context.Request), new MiddlewareContext
                {
                    Host = host,
                    Method = method,
                    Options = options,
                    Request = request,
                    Response = null
                });
            }
    
            public override AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(
                Method<TRequest, TResponse> method, string host, CallOptions options, TRequest request)
            {
                return Call((context) => base.AsyncUnaryCall((Method<TRequest, TResponse>)context.Method, context.Host, context.Options, (TRequest)context.Request), new MiddlewareContext
                {
                    Host = host,
                    Method = method,
                    Options = options,
                    Request = request,
                    Response = null
                });
            }
    
            public override AsyncServerStreamingCall<TResponse> AsyncServerStreamingCall<TRequest, TResponse>(Method<TRequest, TResponse> method, string host, CallOptions options,
                TRequest request)
            {
                return Call((context) => base.AsyncServerStreamingCall((Method<TRequest, TResponse>)context.Method, context.Host, context.Options, (TRequest)context.Request), new MiddlewareContext
                {
                    Host = host,
                    Method = method,
                    Options = options,
                    Request = request,
                    Response = null
                });
            }
    
            public override AsyncClientStreamingCall<TRequest, TResponse> AsyncClientStreamingCall<TRequest, TResponse>(Method<TRequest, TResponse> method, string host, CallOptions options)
            {
                return Call((context) => base.AsyncClientStreamingCall((Method<TRequest, TResponse>)context.Method, context.Host, context.Options), new MiddlewareContext
                {
                    Host = host,
                    Method = method,
                    Options = options,
                    Request = null,
                    Response = null
                });
            }
    
            public override AsyncDuplexStreamingCall<TRequest, TResponse> AsyncDuplexStreamingCall<TRequest, TResponse>(Method<TRequest, TResponse> method, string host, CallOptions options)
            {
                return Call((context) => base.AsyncDuplexStreamingCall((Method<TRequest, TResponse>)context.Method, context.Host, context.Options), new MiddlewareContext
                {
                    Host = host,
                    Method = method,
                    Options = options,
                    Request = null,
                    Response = null
                });
            }
        }
    到这里基于管道的中间件基本完成。接下来就是在客户端使用了
     var pipeline = new PipelineBuilder()
                  //.Use<ExceptionMiddleware>()
                  //.Use<TimerMiddleware>()
                  //.Use<LoggingMiddleware>()
                  //.Use<TimeoutMiddleware>()
                  .Use<PolicyMiddleware>(new PolicyMiddlewareOptions
                  {
                      RetryTimes = 2,
                      TimoutMilliseconds = 100
                  })
                  ;
                //pipeline.Use<LoggingMiddleware>();// pipeline.UseWhen<LoggingMiddleware>(ctx => ctx.Context.Method.EndsWith("SayHello"));
                //pipeline.Use<TimeoutMiddleware>(new TimeoutMiddlewareOptions { TimoutMilliseconds = 1000 });
                //console logger
                pipeline.Use(async (ctx, next) =>
                {
                    Console.WriteLine(ctx.Request.ToString());
                    await next(ctx);
                    Console.WriteLine(ctx.Response.ToString());
                });
    
                Channel channel = new Channel("127.0.0.1:50051", ChannelCredentials.Insecure);
                MiddlewareCallInvoker callInvoker = new MiddlewareCallInvoker(channel, pipeline.Build());
    
                var clientApi = new Get.GetClient(callInvoker);
    
                var replyApi = clientApi.SayHello(new ContractsSample1.HelloApiDemo.HelloRequest { Name = " APII" });
                Console.WriteLine("Greeting: " + replyApi.Message);
    

      Middleware

     public class LoggingMiddleware
        {
            private PipelineDelagate _next;
    
            public LoggingMiddleware(PipelineDelagate next)
            {
                _next = next;
            }
    
            public async Task Invoke(MiddlewareContext context)
            {
               //Before
                await _next(context);
                //End
            }
        }
    

      至此已完成所有工作。希望能够帮助到大家。

    如果有机会将给大家介绍gRPC跨平台网关,该网关能够实现自动将下游的gRPC服务转化成http api,只需要你的proto文件即可。如果你使用consul,甚至无需你做任务配置。

  • 相关阅读:
    组合模式(Composition)
    [ASP.NET MVC 3 系列] ASP.NET MVC 3 Beta新特性以及.Net开源的趋势最新译文
    [ASP.NET MVC2 系列] Action Filters以及自定义OutputCache ActionFilterAttribute事件发生次序
    [原创]CLR GC垃圾收集过程模拟(用C#来显示垃圾收集过程的视觉效果)
    [ASP.NET MVC 专题] ViewEngine的发展以及应用
    [ASP.NET MVC 专题] 如何为Route构造相关的自定义Configuration
    效率最高的Excel数据导入(c#调用SSIS Package将数据库数据导入到Excel文件中【附源代码下载】)
    ASP.NET页面生命周期的整体把握
    效率最高的Excel数据导入续SSIS Package包制作图解全过程
    Flex父子窗口传值
  • 原文地址:https://www.cnblogs.com/kingreatwill/p/9501086.html
Copyright © 2020-2023  润新知