最近 GRPC 很火, 感觉整 RPC 不用 GRPC 都快跟不上时髦了.
gRPC 设计
gRPC 是一种与语言无关的高性能远程过程调用 (RPC) 框架. 刚好需要使用一个的 RPC 应用系统, 自然而然就盯上了它, 但是它真能够解决所有问题吗? 不见得, 先看看他的优点:
gRPC 的主要优点:
现代高性能轻量级 RPC 框架.
协定优先 API 开发, 默认使用协议缓冲区, 允许与语言无关的实现.
可用于多种语言的工具, 以生成强类型服务器和客户端.
支持客户端, 服务器和双向流式处理调用.
使用 Protobuf 二进制序列化减少对网络的使用.
对应的适用场景:
微服务: gRPC 设计用于低延迟和高吞吐量通信. gRPC 对于效率至关重要的轻量级微服务非常有用.
点对点实时通信: gRPC 对双向流式传输提供出色的支持. gRPC 服务可以实时推送消息而无需轮询.
多语言环境: gRPC 工具支持所有常用的开发语言, 因此, gRPC 是多语言环境的理想选择.
网络受限环境: gRPC 消息使用 Protobuf(一种轻量级消息格式)进行序列化. gRPC 消息始终小于等效的 JSON 消息.
gRPC 还是有缺点的:
浏览器支持受限: 绝大数浏览器不支持 HTTP/2
非人工可读取: proto 文件规定的格式在通讯中会序列化成二进制数据, 人工解析较为困难.
不适用的场景与替代:
浏览器可访问的 API:gRPC 在浏览器中未受到完全支持. gRPC-web 可以提供浏览器支持, 但它具有局限性并引入了服务器代理.
广播实时通信: gRPC 支持通过流式传输进行实时通信, 但不存在将消息广播到注册连接的概念. 例如, 在聊天室方案中, 应将新的聊天消息发送到聊天室中的所有客户端, 这要求每个 gRPC 调用将新的聊天消息单独流式传输到客户端. SignalR 是适用于此方案的框架. SignalR 具有持久性连接的概念, 并内置对广播消息的支持.
进程间通信: 进程必须托管 HTTP/2 服务器才能接受传入的 gRPC 调用. 对于 Windows, 进程间通信管道是一种快速, 轻便的通信方法.
目标分析
我需要有一个能够实现远程调用的好办法, 系统支持 Windows 就好, 最好性能高一些(数据量大), 程序小一点, 但是我也不想直接处理二进制数据流(最好能有封装的框架).
考虑进程通信常用的:
信号 / 信号量: 简单, 能够承载的消息内容较少.
消息队列: 支持消息, 功能较为强大.
共享内存: 性能最强, 但只限于单机.
管道: 性能较强, 但是只支持 stream.
Socket: 最灵活, 但是需要有网卡.
首先排除信号 / 信号量, 处理的信息量太小了; 然后共享内存也排除, 只能单机不符合我的要求; 剩下的三个似乎都可以满足要求, 可以在这个基础上建立 RPC, 而 gRPC 就是建立在 socket(HTTP/2)上的, 就像上面讲的, 要自己集成一个 HTTP/2 服务器 (比如 Kestrel) 才行, 不够轻量化; 剩下的两个 Windows 都有內建支持, 可以考虑一下.
本着拿来主义的思想, 我在 GitHub 上找到一个 https://github.com/cyanfish/grpc-dotnet-namedpipes , 支持在命名管道上实现 gRPC, 相当于在 stream 上封装了一层, 不用直接处理二进制数据流了.
用作者自己的话来说, 这么做相较于普通的 gRPC 有几个优点:
更优秀的访问控制;
纯. NET 库, 不需要带 ASP.NET Core 或者超过 3MB 的非托管库依赖;
启动时间更快
2-3 倍的数据吞吐量
没有防火墙警告
不需要网卡
实现
建立一个 proto
创建一个. NET Library
添加一个 proto 文件, 可以直接使用微软的简单例子
- syntax = "proto3";
- service Greeter {
- rpc SayHello (HelloRequest) returns (HelloReply);
- }
- message HelloRequest {
- string name = 1;
- }
- message HelloReply {
- string message = 1;
- }
添加 nuget 包: Google.Protobuf,Grpc.Core,Grpc.Tools
单击 proto 文件, 在属性对话框, 选择生成操作为 Protobuf Compiler
建立 Client
新建一个 Console 程序, 添加上面的项目引用, 输入以下代码:
- var server = new NamedPipeServer("MY_PIPE_NAME");
- Greeter.BindService(server.ServiceBinder, new GreeterService());
- server.Start();
添加 GrpcDotNetNamedPipes 的 nuget 依赖:
Install-Package GrpcDotNetNamedPipes
建立 Server
再新建一个 Console 程序, 添加上面的项目引用, 也添加那个 nuget 依赖和一些别的依赖, 输入以下代码:
- var channel = new NamedPipeChannel(".", "MY_PIPE_NAME");
- var client = new Greeter.GreeterClient(channel);
- var response = await client.SayHelloAsync(
- new HelloRequest { Name = "World" });
- Console.WriteLine(response.Message);
然后运行就能看见熟悉的 Hello World 了, 用起来和 gRPC 的标准实现没太大区别.
总结
完整代码见 https://github.com/circler3/gRPC_Demo .
这种方式也有它的局限性, 首先是 Windows 的命名管道与 Linux 上面的实现是不同的, 所以并不能实现直接跨平台通讯; 然后就是这个对于其他语言的开发的 gRPC 也不是完全兼容的, 需要其他语言开发的程序也做命名管道的适配才行, 换言之, 它不是通用标准. 所以, 对于一般的 gRPC 应用, 还是更推荐使用标准实现.
参考
.NET Core 上的 gRPC 的简介
比较 gRPC 服务和 HTTP API
关于 IPC 性能的提问
Linux 上各种 IPC 性能比较 https://github.com/goldsborough/ipc-bench
来源: https://www.cnblogs.com/podolski/p/13282975.html