NET Core 3.0 使用gRPC

一.前言

在前一文 《ASP.NET Core 3.0 使用gRPC》中有提到 gRPC 支持双向流调用,支持实时推送消息,这也是 gRPC的一大特点,且 gRPC 在对双向流的控制支持上也是非常强大的。

二. 什么是 gRPC 流

gRPC 有四种服务类型,分别是:简单 RPC(Unary RPC)、服务端流式 RPC (Server streaming RPC)、客户端流式 RPC (Client streaming RPC)、双向流式 RPC(Bi-directional streaming RPC)。它们主要有以下特点:

服务类型 特点
简单 RPC 一般的rpc调用,传入一个请求对象,返回一个返回对象
服务端流式 RPC 传入一个请求对象,服务端可以返回多个结果对象
客户端流式 RPC 客户端传入多个请求对象,服务端返回一个结果对象
双向流式 RPC 结合客户端流式RPC和服务端流式RPC,可以传入多个请求对象,返回多个结果对象

三.为什么 gRPC 支持流

gRPC 通信是基于 HTTP/2 实现的,它的双向流映射到 HTTP/2 流。HTTP/2 具有流的概念,流是为了实现HTTP/2的多路复用。流是服务器和客户端在HTTP/2连接内用于交换帧数据的独立双向序列,逻辑上可看做一个较为完整的交互处理单元,即表达一次完整的资源请求、响应数据交换流程;一个业务处理单元,在一个流内进行处理完毕,这个流生命周期完结。

特点如下:

  • 一个HTTP/2连接可同时保持多个打开的流,任一端点交换帧
  • 流可被客户端或服务器单独或共享创建和使用
  • 流可被任一端关闭
  • 在流内发送和接收数据都要按照顺序
  • 流的标识符自然数表示,1~2^31-1区间,有创建流的终端分配
  • 流与流之间逻辑上是并行、独立存在

摘自 HTTP/2笔记之流和多路复用 by 聂永

四.gRPC中使用双向流调用

我们在前文中编写的RPC属于简单RPC,没有包含流调用,下面直接讲一下双向流,根据第二小节列举的四种服务类型,如果我们掌握了简单RPC和双向流RPC,那么服务端流式 RPC和客户端流式 RPC自然也就没有问题了。

这里我们继续使用前文的代码,要实现的目标是一次给多个猫洗澡。

① 首先在 LuCat.proto 定义两个rpc,一个 Count 用于统计猫的数量,一个 双向流 RPC BathTheCat 用于给猫洗澡

  1. syntax = "proto3";
  2. option csharp_namespace = "AspNetCoregRpcService";
  3. import "google/protobuf/empty.proto";
  4. package LuCat; //定义包名
  5. //定义服务
  6. service LuCat{
  7. //定义给猫洗澡双向流rpc
  8. rpc BathTheCat(stream BathTheCatReq) returns ( stream BathTheCatResp);
  9. //定义统计猫数量简单rpc
  10. rpc Count(google.protobuf.Empty) returns (CountCatResult);
  11. }
  12. message SuckingCatResult{
  13. string message=1;
  14. }
  15. message BathTheCatReq{
  16. int32 id=1;
  17. }
  18. message BathTheCatResp{
  19. string message=1;
  20. }
  21. message CountCatResult{
  22. int32 Count=1;
  23. }

② 添加服务的实现

这里安利下Resharper,非常方便

  1. private readonly ILogger<LuCatService> _logger;
  2. private static readonly List<string> Cats=new List<string>(){"英短银渐层","英短金渐层","美短","蓝猫","狸花猫","橘猫"};
  3. private static readonly Random Rand=new Random(DateTime.Now.Millisecond);
  4. public LuCatService(ILogger<LuCatService> logger)
  5. {
  6. _logger = logger;
  7. }
  8. public override async Task BathTheCat(IAsyncStreamReader<BathTheCatReq> requestStream, IServerStreamWriter<BathTheCatResp> responseStream, ServerCallContext context)
  9. {
  10. var bathQueue=new Queue<int>();
  11. while (await requestStream.MoveNext())
  12. {
  13. //将要洗澡的猫加入队列
  14. bathQueue.Enqueue(requestStream.Current.Id);
  15. _logger.LogInformation($"Cat {requestStream.Current.Id} Enqueue.");
  16. }
  17. //遍历队列开始洗澡
  18. while (bathQueue.TryDequeue(out var catId))
  19. {
  20. await responseStream.WriteAsync(new BathTheCatResp() { Message = $"铲屎的成功给一只{Cats[catId]}洗了澡!" });
  21. await Task.Delay(500);//此处主要是为了方便客户端能看出流调用的效果
  22. }
  23. }
  24. public override Task<CountCatResult> Count(Empty request, ServerCallContext context)
  25. {
  26. return Task.FromResult(new CountCatResult()
  27. {
  28. Count = Cats.Count
  29. });
  30. }

BathTheCat 方法会接收多个客户端发来的CatId,然后将他们加入队列中,等客户端发送完成后开始依次洗澡并返回给客户端。

③ 客户端实现

随机发送10个猫Id给服务端,然后接收10个洗澡结果。

  1. var channel = GrpcChannel.ForAddress("https://localhost:5001");
  2. var catClient = new LuCat.LuCatClient(channel);
  3. //获取猫总数
  4. var catCount = await catClient.CountAsync(new Empty());
  5. Console.WriteLine($"一共{catCount.Count}只猫。");
  6. var rand = new Random(DateTime.Now.Millisecond);
  7. var bathCat = catClient.BathTheCat();
  8. //定义接收吸猫响应逻辑
  9. var bathCatRespTask = Task.Run(async() =>
  10. {
  11. await foreach (var resp in bathCat.ResponseStream.ReadAllAsync())
  12. {
  13. Console.WriteLine(resp.Message);
  14. }
  15. });
  16. //随机给10个猫洗澡
  17. for (int i = 0; i < 10; i++)
  18. {
  19. await bathCat.RequestStream.WriteAsync(new BathTheCatReq() {Id = rand.Next(0, catCount.Count)});
  20. }
  21. //发送完毕
  22. await bathCat.RequestStream.CompleteAsync();
  23. Console.WriteLine("客户端已发送完10个需要洗澡的猫id");
  24. Console.WriteLine("接收洗澡结果:");
  25. //开始接收响应
  26. await bathCatRespTask;
  27. Console.WriteLine("洗澡完毕");

④ 运行

可以看到双向流调用成功,客户端发送了10个猫洗澡请求对象,服务端返回了10个猫洗澡结果对象。且是实时推送的,这就是 gRPC 的双向流调用。

这里大家可以自行改进来演变成客户端流式或者服务端流式调用。客户端发送一个猫Id列表,然后服务端返回每个猫洗澡结果,这就是服务端流式调用。客户端依次发送猫Id,然后服务端一次性返回所有猫的洗澡结果(给所有猫洗澡看做是一个业务,返回这个业务的结果),就是客户端流式调用。这里我就不再演示了。

五.流控制

gRPC 的流式调用支持对流进行主动取消的控制,进而可以衍生出流超时限制等控制。

在流式调用是,可以传一个 CancellationToken 参数,它就是我们用来对流进行取消控制的:

改造一下我们在第四小节的代码:

① 客户端

  1. var cts = new CancellationTokenSource();
  2. //指定在2.5s后进行取消操作
  3. cts.CancelAfter(TimeSpan.FromSeconds(2.5));
  4. var bathCat = catClient.BathTheCat(cancellationToken: cts.Token);
  5. //定义接收吸猫响应逻辑
  6. var bathCatRespTask = Task.Run(async() =>
  7. {
  8. try
  9. {
  10. await foreach (var resp in bathCat.ResponseStream.ReadAllAsync())
  11. {
  12. Console.WriteLine(resp.Message);
  13. }
  14. }
  15. catch (RpcException ex) when (ex.StatusCode == StatusCode.Cancelled)
  16. {
  17. Console.WriteLine("Stream cancelled.");
  18. }
  19. });

② 服务端

  1. //遍历队列开始洗澡
  2. while (!context.CancellationToken.IsCancellationRequested && bathQueue.TryDequeue(out var catId))
  3. {
  4. _logger.LogInformation($"Cat {catId} Dequeue.");
  5. await responseStream.WriteAsync(new BathTheCatResp() { Message = $"铲屎的成功给一只{Cats[catId]}洗了澡!" });
  6. await Task.Delay(500);//此处主要是为了方便客户端能看出流调用的效果
  7. }

③ 运行

设置的是双向流式调用2.5s后取消流,从客户端调用结果看到,并没有收到全部10个猫的洗澡返回结果,流就已经被取消了,这就是 gRPC 的流控制。

六.结束

这里流式调用可以实现实时推送,服务端到客户端或者客户端到服务端短实时推送消息,但是这个和传统意义上的长连接主动推送、广播消息不一样,不管你是服务端流式、客户端流式还是双向流式,必须要由客户端进行发起,通过客户端请求来建立流通信。

七.参考资料

ASP.NET Core 3.0 使用 gRPC无法编译问题

NET Core3高性能RPC框架的更多相关文章

  1. 基于Protobuf的分布式高性能RPC框架——Navi-Pbrpc

    基于Protobuf的分布式高性能RPC框架——Navi-Pbrpc 二月 8, 2016 1 简介 Navi-pbrpc框架是一个高性能的远程调用RPC框架,使用netty4技术提供非阻塞.异步.全 ...

  2. C# -- 高性能RPC框架:Socean.RPC

    简介 Socean.RPC是一个.Net下的高性能RPC框架,框架以高性能.高稳定性为目标,底层基于socket,无第三方库引用,代码简洁,总代码量大约在2000行,框架性能较高,在普通PC上测试,长 ...

  3. Google 高性能 RPC 框架 gRPC 1.0.0 发布(附精彩评论)

    gRPC是一个高性能.开源.通用的RPC框架,面向移动和HTTP/2设计,是由谷歌发布的首款基于Protocol Buffers的RPC框架. gRPC基于HTTP/2标准设计,带来诸如双向流.流控. ...

  4. 【架构】Twitter高性能RPC框架Finagle介绍

    Twitter的RPC框架Finagle简介 Finagle是Twitter基于Netty开发的支持容错的.协议无关的RPC框架,该框架支撑了Twitter的核心服务.来自Twitter的软件工程师J ...

  5. GRPC 1.3.4 发布,Google 高性能 RPC 框架(Java C++ Go)

    GRPC 1.3.4 发布了,GRPC 是一个高性能.开源.通用的 RPC 框架,面向移动和 HTTP/2 设计,是由谷歌发布的首款基于 Protocol Buffers 的 RPC 框架. GRPC ...

  6. 一个高性能RPC框架的连接管理

    既然说连接,先对EpollServer的连接管理做个介绍吧.客户端与服务器一次conn,被封装成为Connection类在服务器进行管理. 服务器连接有三种类型,分别为: enum EnumConne ...

  7. 主流的RPC框架有哪些

    RPC是远程过程调用的简称,广泛应用在大规模分布式应用中,作用是有助于系统的垂直拆分,使系统更易拓展.Java中的RPC框架比较多,各有特色,广泛使用的有RMI.Hessian.Dubbo等.RPC还 ...

  8. 分布式远程服务调用(RPC)框架

    分布式远程服务调用(RPC)框架 finagle:一个支持容错,协议无关的RPC系统 热门度(没变化) 10.0 活跃度(没变化) 10.0  Watchers:581 Star:6174 Fork: ...

  9. 精通并发与 Netty (二)常用的 rpc 框架

    Google Protobuf 使用方式分析 对于 RPC 协议来说,最重要的就是对象的发送与接收,这就要用到序列化与反序列化,也称为编码和解码,序列化与反序列化和网络传输一般都在对应的 RPC 框架 ...

随机推荐

  1. 用Wget下载的文件在哪里可以找到。。

    输入命令: cd ~ 然后 ls 就ok了.

  2. 手写队列以及stl中队列的使用

    一,手写队列. struct queue { ; ,rear=,a[maxn]; void push(int x) { a[++rear]=x; } void pop() { first++; } i ...

  3. L1731

    生日蛋糕 输入的东西,一个是蛋糕的体积,一个是蛋糕的层数, 简言之,我觉得这个就是两个dfs的状态. 一旦越过这两个就得return ,同时这两个东西也参与进去了dfs. 至于题目, 第一个要求是层数 ...

  4. Chocolatey 方便的windows 包管理工具

    windows 在包管理上一般大家都是网上下载二进制文件或者就是通过软件管家进行安装,这些对于开发人员可能就有点不是 很专业了, Chocolatey 是一个不错的windows 软件包管理工具 安装 ...

  5. circus && web comsole docker-compose 独立部署

    问题的根本原因是web console 的bug(实际上还是python 对于依赖版本出来不明确) circus 进程docker 镜像 dockerfile FROM python:slim-str ...

  6. walk

    一个go做gui的包, 可以配置程序图标 编译运行需要...fest文件 rsrc

  7. CF1098E Fedya the Potter

    CF1098E Fedya the Potter 题意:有一个序列\(A\). 对所有\(1\leq l\leq r\leq |A|\),将\(\gcd_{i=l}^{r}A_i\)加入\(B\)中. ...

  8. vue-cli3整体迁移至服务端渲染nuxtjs

    vue项目与nuxt.js实在有着太多的不同,例如项目结构变化很大,router.js没了,vuex store写法有变化,router钩子没了等等.老项目毕竟也有一些体量,这么折腾我可接受不了,不过 ...

  9. Web前端开发(高级)下册-目录

    多媒体与绘图 多媒体音频格式视频格式 HTML5多媒体支持 <audio>和<video> htmlaudioElement和htmlVideoElement <audi ...

  10. 使用docker 基于centos7制作mysql镜像

    说明:由于业务需要使用centos7.6+mysql5.7+jdk8以及其他的java程序,本想在网上找一个现成的,发现镜像都不适合我. 一.yum方式安装mysql 1.编写dockerfile文件 ...