ASP.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 用于给猫洗澡
syntax = "proto3";
option csharp_namespace = "AspNetCoregRpcService";
import "google/protobuf/empty.proto";
package LuCat; //定义包名
//定义服务
service LuCat{
//定义给猫洗澡双向流rpc
rpc BathTheCat(stream BathTheCatReq) returns ( stream BathTheCatResp);
//定义统计猫数量简单rpc
rpc Count(google.protobuf.Empty) returns (CountCatResult);
}
message SuckingCatResult{
string message=1;
}
message BathTheCatReq{
int32 id=1;
}
message BathTheCatResp{
string message=1;
}
message CountCatResult{
int32 Count=1;
}
② 添加服务的实现
这里安利下Resharper,非常方便
private readonly ILogger<LuCatService> _logger;
private static readonly List<string> Cats=new List<string>(){"英短银渐层","英短金渐层","美短","蓝猫","狸花猫","橘猫"};
private static readonly Random Rand=new Random(DateTime.Now.Millisecond);
public LuCatService(ILogger<LuCatService> logger)
{
_logger = logger;
}
public override async Task BathTheCat(IAsyncStreamReader<BathTheCatReq> requestStream, IServerStreamWriter<BathTheCatResp> responseStream, ServerCallContext context)
{
var bathQueue=new Queue<int>();
while (await requestStream.MoveNext())
{
//将要洗澡的猫加入队列
bathQueue.Enqueue(requestStream.Current.Id);
_logger.LogInformation($"Cat {requestStream.Current.Id} Enqueue.");
}
//遍历队列开始洗澡
while (bathQueue.TryDequeue(out var catId))
{
await responseStream.WriteAsync(new BathTheCatResp() { Message = $"铲屎的成功给一只{Cats[catId]}洗了澡!" });
await Task.Delay(500);//此处主要是为了方便客户端能看出流调用的效果
}
}
public override Task<CountCatResult> Count(Empty request, ServerCallContext context)
{
return Task.FromResult(new CountCatResult()
{
Count = Cats.Count
});
}
BathTheCat 方法会接收多个客户端发来的CatId,然后将他们加入队列中,等客户端发送完成后开始依次洗澡并返回给客户端。
③ 客户端实现
随机发送10个猫Id给服务端,然后接收10个洗澡结果。
var channel = GrpcChannel.ForAddress("https://localhost:5001");
var catClient = new LuCat.LuCatClient(channel);
//获取猫总数
var catCount = await catClient.CountAsync(new Empty());
Console.WriteLine($"一共{catCount.Count}只猫。");
var rand = new Random(DateTime.Now.Millisecond);
var bathCat = catClient.BathTheCat();
//定义接收吸猫响应逻辑
var bathCatRespTask = Task.Run(async() =>
{
await foreach (var resp in bathCat.ResponseStream.ReadAllAsync())
{
Console.WriteLine(resp.Message);
}
});
//随机给10个猫洗澡
for (int i = 0; i < 10; i++)
{
await bathCat.RequestStream.WriteAsync(new BathTheCatReq() {Id = rand.Next(0, catCount.Count)});
}
//发送完毕
await bathCat.RequestStream.CompleteAsync();
Console.WriteLine("客户端已发送完10个需要洗澡的猫id");
Console.WriteLine("接收洗澡结果:");
//开始接收响应
await bathCatRespTask;
Console.WriteLine("洗澡完毕");
④ 运行
可以看到双向流调用成功,客户端发送了10个猫洗澡请求对象,服务端返回了10个猫洗澡结果对象。且是实时推送的,这就是 gRPC 的双向流调用。
这里大家可以自行改进来演变成客户端流式或者服务端流式调用。客户端发送一个猫Id列表,然后服务端返回每个猫洗澡结果,这就是服务端流式调用。客户端依次发送猫Id,然后服务端一次性返回所有猫的洗澡结果(给所有猫洗澡看做是一个业务,返回这个业务的结果),就是客户端流式调用。这里我就不再演示了。
五.流控制
gRPC 的流式调用支持对流进行主动取消的控制,进而可以衍生出流超时限制等控制。
在流式调用是,可以传一个 CancellationToken 参数,它就是我们用来对流进行取消控制的:
改造一下我们在第四小节的代码:
① 客户端
var cts = new CancellationTokenSource();
//指定在2.5s后进行取消操作
cts.CancelAfter(TimeSpan.FromSeconds(2.5));
var bathCat = catClient.BathTheCat(cancellationToken: cts.Token);
//定义接收吸猫响应逻辑
var bathCatRespTask = Task.Run(async() =>
{
try
{
await foreach (var resp in bathCat.ResponseStream.ReadAllAsync())
{
Console.WriteLine(resp.Message);
}
}
catch (RpcException ex) when (ex.StatusCode == StatusCode.Cancelled)
{
Console.WriteLine("Stream cancelled.");
}
});
② 服务端
//遍历队列开始洗澡
while (!context.CancellationToken.IsCancellationRequested && bathQueue.TryDequeue(out var catId))
{
_logger.LogInformation($"Cat {catId} Dequeue.");
await responseStream.WriteAsync(new BathTheCatResp() { Message = $"铲屎的成功给一只{Cats[catId]}洗了澡!" });
await Task.Delay(500);//此处主要是为了方便客户端能看出流调用的效果
}
③ 运行
设置的是双向流式调用2.5s后取消流,从客户端调用结果看到,并没有收到全部10个猫的洗澡返回结果,流就已经被取消了,这就是 gRPC 的流控制。
六.结束
这里流式调用可以实现实时推送,服务端到客户端或者客户端到服务端短实时推送消息,但是这个和传统意义上的长连接主动推送、广播消息不一样,不管你是服务端流式、客户端流式还是双向流式,必须要由客户端进行发起,通过客户端请求来建立流通信。
七.参考资料
GRPC的四种服务类型 by twtydgo
HTTP/2笔记之流和多路复用 by 聂永
ASP.NET Core 3.0 gRPC 双向流的更多相关文章
- ASP.NET Core 3.0 gRPC 拦截器
目录 ASP.NET Core 3.0 使用gRPC ASP.NET Core 3.0 gRPC 双向流 ASP.NET Core 3.0 gRPC 拦截器 一. 前言 前面两篇文章给大家介绍了使用g ...
- ASP.NET Core 3.0 gRPC 身份认证和授权
一.开头聊骚 本文算是对于 ASP.NET Core 3.0 gRPC 研究性学习的最后一篇了,以后在实际使用中,可能会发一些经验之文.本文主要讲 ASP.NET Core 本身的认证授权和gRPC接 ...
- asp.net core 3.0 gRPC框架小试
什么是gRPC gRPC是google开源的一个高性能.跨语言的RPC框架,基于HTTP2协议,采用ProtoBuf 定义的IDL. gRPC 的主要优点是: 现代高性能轻量级 RPC 框架. 协定优 ...
- ASP.NET Core 3.0 gRPC 配置使用HTTP
前言 gRPC是基于http/2,是同时支持https和http协议的,我们在gRPC实际使用中,在内网通讯场景下,更多的是走http协议,达到更高的效率,下面介绍如何在 .NET Core 3.0 ...
- ASP.NET Core 3.0 使用gRPC
一.简介 gRPC 是一个由Google开源的,跨语言的,高性能的远程过程调用(RPC)框架. gRPC使客户端和服务端应用程序可以透明地进行通信,并简化了连接系统的构建.它使用HTTP/2作为通信协 ...
- ASP.NET Core 3.0 上的gRPC服务模板初体验(多图)
早就听说ASP.NET Core 3.0中引入了gRPC的服务模板,正好趁着家里电脑刚做了新系统,然后装了VS2019的功夫来体验一把.同时记录体验的过程.如果你也想按照本文的步骤体验的话,那你得先安 ...
- .NET Core 3.0及ASP.NET Core 3.0 前瞻
前几天微软发布了 .NET Core 3.0 Preview 9 ,这是.NET Core 3.0 最后一个预览版. .NET Core 3.0 正式发布将在.NET Conf 上发布,.NET Co ...
- [翻译] ASP.NET Core 3.0 的新增功能
ASP.NET Core 3.0 的新增功能 全文翻译自微软官方文档英文版 What's new in ASP.NET Core 3.0 本文重点介绍了 ASP.NET Core 3.0 中最重要的更 ...
- ASP.NET Core 3.0 入门
原文:ASP.NET Core 3.0 入门 课程简介 与2.x相比发生的一些变化,项目结构.Blazor.SignalR.gRPC等 课程预计结构 ASP.NET Core 3.0项目架构简介 AS ...
随机推荐
- java 局部变量成员变量区别
首先,成员变量可直接初始化(即赋值),也可不赋值,不赋值java按照以下类型自动赋值 局部变量调用前必须初始化(赋值),java不会自动处理 局部变量可以和成员变量重名(但不建议),如果在当前对象的方 ...
- DIY显示器篇------DIY教程
前言: DIY显示器是这几年才火起来的,或者说这几年在游戏圈火起来的.我第一次看到是在NGA上,一位玩PUBG的大佬自己DIY了一个显示器,27寸 2k 144 ips的屏幕,当时市面上只有四款显示器 ...
- idea(java)实用开发插件
Idea常用的插件: mybatisX, ---------------- (Alt + enter) codeGlace, Lombok, sonarlint, translation, ...
- 90001PS相关操作
第一章 PS基础操作 1.1 PS界面介绍 (1)界面包含:菜单栏.状态样式栏.工具栏.绘图区域.工作区. (2)布局可以在左上角进行切换,区分主要为工作区的侧重点不同. (3)布局想恢复可在窗口 ...
- Selenium3 + Python3自动化测试系列六——等待方式
等待 一.等待是什么,为什么要等待 在做自动化测试,设计测试用例的时候,有时下一步的操作会依赖上一步的结果或者内容,上一步操作成功之后才能进行下一步操作等, 这时候,我们就需要使用等待,来判断上一步操 ...
- lua_lua与.Net互相调用
配置环境:创建C#项目,引入luainterface-1.5.3\Built下面的LuaInterface.dll文件和luanet.dll文件.引入命名空间using LuaInterface 代码 ...
- requests.get爬虫模块参数
地址和请求头参数--url和header res = requests.get(url,headers=headers) 向网站发起请求,并获取响应对象 参数 url :需要抓取的URL地址 head ...
- abp(net core)+easyui+efcore实现仓储管理系统——菜单 (十六)
系统目录 abp(net core)+easyui+efcore实现仓储管理系统——ABP总体介绍(一) abp(net core)+easyui+efcore实现仓储管理系统——解决方案介绍(二) ...
- JAVA解除tomcat 对浏览器特别字符 | () {} [] 的限制
1.打开tomcat本机地址打开conf文件夹(一定要关闭Tomcat启动在修改) 2.记事本打开或者编辑软件打开(我这里推荐的编辑软件是以下如图) 打开进去加入这两行代码 3打开server.xml ...
- 【HDU5409】CRB and Graph 边双联通 子树最值
HDU # 题意 有一个简单图,n个点,m条边.对于每条割边,求出删去这条边后,在两个联通块中各取一个u,v.使得u<v,并且u尽量大而v尽量小. # 思路 求出边双联通是肯定的. 答案的限制条 ...