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,甚至无需你做任务配置。
gRPC Client Middleware.的更多相关文章
- gRPC Client的负载均衡器
一.gRPC是什么? gRPC是一个高性能.通用的开源RPC框架,其由Google主要面向移动应用开发并基于HTTP/2协议标准而设计,基于ProtoBuf(Protocol Buffers)序列化协 ...
- Building gRPC Client iOS Swift Note Taking App
gRPC is an universal remote procedure call framework developed by Google that has been gaining inter ...
- yoyogo v1.7.4 发布,支持 grpc v1.3.8 & etcd 3.5.0
YoyoGo (Go语言框架)一个简单.轻量.快速.基于依赖注入的微服务框架( web .grpc ),支持Nacos/Consoul/Etcd/Eureka/k8s /Apollo等 . https ...
- gRPC源码分析1-SSL/TLS
引子 前几天看到微信后台团队分享了TLS相关文章,正好gRPC里TLS数据加密是很重要的一块,于是整理出了这篇文章. 在gRPC里,如果仅仅是用来做后端微服务,可以考虑不加密.本文太长,先给个大纲. ...
- gRPC .NET Core跨平台学习
前些天发布gRPC C# 学习,在.NET Framework 中使用gRPC ,今天来学习 .NET Core gRPC. gRPC 的.NET Core 包在NuGet 上发布了,结合.NET C ...
- gRPC helloworld service, RESTful JSON API gateway and swagger UI
概述 本篇博文完整讲述了如果通过 protocol buffers 定义并启动一个 gRPC 服务,然后在 gRPC 服务上提供一个 RESTful JSON API 的反向代理 gateway,最后 ...
- 开始食用grpc(之一)
开始食用grpc(之一) 转载请注明出处:https://www.cnblogs.com/funnyzpc/p/9501353.html ``` 记一次和一锅们压马路,路过一咖啡厅(某巴克),随口 ...
- gRPC 在 Python中的应用
python -m grpc_tools.protoc --proto_path=. --python_out=. --grpc_python_out=. hello.proto 简介 在python ...
- Abp + gRpc 如何实现用户会话状态传递
0.背景 在实际项目当中,我们采用的是 Abp 框架,但是 Abp 框架官方并没有针对 Grpc 进行模块封装.基于此我结合 Abp 与 MagicOnion 封装了一个 Abp.Grpc 模块,它包 ...
随机推荐
- 【长文】Google面试官分步解析自己泄漏前的面试题,超多干货和建议
本文翻译自Google工程师/面试官Alex Golec的文章:Google Interview Questions Deconstructed: The Knight's Dialer:翻译:实验楼 ...
- locust压测websocket协议
Locust是以HTTP为主要目标构建的. 但是,通过编写触发器request_success和 request_failure事件的自定义客户端,可以轻松扩展到任何基于请求/响应的系统的负载测试 . ...
- new Date() IE浏览器下不起做用的解决方法
因为需要计算两个时间之间的差值,所以我用了new Date().getTime()来算的两个时间的时间戳,后来突然发现IE浏览器算的值是NaN. 下面是我在Ie浏览器下打印的结果: 我发现如果不加时分 ...
- href和src的区别(小计)
1.Src 是指向物件的来源地址,请求src资源时会将其指向的资源下载并应用文档中 src的内容是页面上比不可少的一部分,是引入.在 img.script.iframe 等元素上使用. 2.href ...
- JAVA String中文乱码
System.out.println(str); String str1 = new String(str.getBytes("ISO-8859-1"), "utf-8& ...
- Java -- 基于JDK1.8的ArrayList源码分析
1,前言 很久没有写博客了,很想念大家,18年都快过完了,才开始写第一篇,争取后面每周写点,权当是记录,因为最近在看JDK的Collection,而且ArrayList源码这一块也经常被面试官问道,所 ...
- node概述
1.什么是node:“一个搭建在Chrome JavaScript运行时 上的平台,用于构建高速.可伸缩的网络程序.Node.js采用的事件驱动.非阻塞I/O模型,使它 既轻量又高效,并成为构建运行在 ...
- day21 python之模块和包
一 模块 1 什么是模块? 常见的场景:一个模块就是一个包含了python定义和声明的文件,文件名就是模块名字加上.py的后缀. 但其实import加载的模块分为四个通用类别: 1 使用python编 ...
- EF(二)Model Fiirst
Model First 是先利用某些工具(如VS的EF设计器)设计出可视化的实体数据模型及他们之间的关系,然后再根据这些实体.关系去生成数据库对象及相关代码文件. 一.设计实体数据模型,生成数据库 1 ...
- intellij idea 官方帮助文档翻译(一)安装
安装需求: 硬件需求: 最低2GB内存,推荐4GB内存 最少要有1.5GB的硬盘空间,以及1GB空间用于缓存 最低1024*768的分辨率 软件需求: intellij idea中包含了jre 1.8 ...