引入

gRPC 是谷歌推出的一个高性能优秀的 RPC 框架,基于 HTTP/2 实现。并且该框架对 .NET Core 有着优秀的支持。
最近在做一个项目正好用到了 gRPC,遇到了需要串流传输的问题。

项目创建

首先还是需要安装 .net core sdk,可以去 http://dot.net 下载。这里我使用的是 2.2.103 版本的 sdk。

mkdir RpcStreaming
cd RpcStreaming
dotnet new console
dotnet add package Grpc // 添加 gRPC 包
dotnet add package Grpc.Tools // 添加 gRPC 工具包
dotnet add package Google.Protobuf // 添加 Protobuf 支持

然后为了支持 protobuf 语言,我们需要修改项目配置文件,在项目中引入 .proto 文件以便生成对应的代码。

在 RpcStreaming.csproj 中,加入<Protobuf Include="**/*.proto" />,除此之外还需要启用最新语言支持(C# 7.3),方便我们将 Main 函数直接写为 async 函数,直接设置为最新版本的语言即可,如下所示:
<Project Sdk="Microsoft.NET.Sdk">
...
<PropertyGroup>
...
<LangVersion>latest</LangVersion>
...
</PropertyGroup> <ItemGroup>
...
<Protobuf Include="**/*.proto" />
...
</ItemGroup>
...
</Project>

这里我们使用了 wildcard 语法匹配了项目内的全部 proto 文件用于生成对应的代码。

到这里,项目的创建就完成了。

编写 Proto 文件

我们在项目目录下建立一个 .proto 文件,用于描述 rpc 调用和消息类型。比如:RpcStreaming.proto
内容如下:

 synatx = "proto3";
service RpcStreamingService {
rpc GetStreamContent (StreamRequest) returns (stream StreamContent) {}
}
message StreamRequest {
string fileName = ;
}
message StreamContent {
bytes content = ;
}

做 RPC 请求时,我们向 RPC 服务器发送一个 StreamRequest 的 message,其中包含了文件路径;为了让服务器以流式传输数据,我们在 returns 内加一个 “stream”。

保存后,我们执行一次 dotnet build,这样就会在 ./obj/Debug/netcoreapp2.2下自动生成 RPC 调用和消息类型的代码。

编写 Server 端代码

为了编写 RPC 调用服务端代码,我们需要重写自动生成的 C# 虚函数。
首先我们进入 ./obj/Debug/netcoreapp2.2 看看自动生成了什么代码。
RpcStreaming.cs 中包含消息类型的定义,RpcStreamingGrpc.cs 中包含了对应 rpc 调用的函数原型。
我们查找一下我们刚刚在 proto 文件中声明的 GetStreamContent。
可以在里面找到一个上方文档注释为 “Base class for server-side implementations RpcStreamingServiceBase” 的抽象类 RpcStreamingServiceBase,里面包含了我们要找的东西。

可以找到我们的 GetStreamContent 的默认实现:
public virtual global::System.Threading.Tasks.Task GetStreamContent(global::StreamRequest request, grpc::IServerStreamWriter<global::StreamContent> responseStream, grpc::ServerCallContext context)
{
throw new grpc::RpcException(new grpc::Status(grpc::StatusCode.Unimplemented, ""));
}

这样就简单了,我们新建一个类 RpcServiceImpl,继承 RpcStreamingService.RpcStreamingServiceBase,然后实现对应的方法即可。

为了串流,我们需要将数据流不断写入 response,这里给一个简单的示例。

 using System;
using System.IO;
using System.Threading.Tasks;
using Google.Protobuf;
using Grpc.Core;
namespace RpcStreaming
{
public class RpcStreamingServiceImpl : RpcStreamingService.RpcStreamingServiceBase
{
public override Task GetStreamContent(StreamRequest request, IServerStreamWriter<StreamContent> response, ServerCallContext context)
{
return Task.Run(async () =>
{
using (var fs = File.Open(request.FileName, FileMode.Open)) // 从 request 中读取文件名并打开文件流
{
var remainingLength = fs.Length; // 剩余长度
var buff = new byte[]; // 缓冲区,这里我们设置为 1 Mb
while (remainingLength > ) // 若未读完则继续读取
{
var len = await fs.ReadAsync(buff); // 异步从文件中读取数据到缓冲区中
remainingLength -= len; // 剩余长度减去刚才实际读取的长度 // 向流中写入我们刚刚读取的数据
await response.WriteAsync(new StreamContent
{
Content = ByteString.CopyFrom(buff, , len)
});
}
}
});
}
}
}

启动 RPC Server

首先需要:

 using Google.Protobuf;
using Grpc.Core;

然后我们在 Main 函数中构建并启动 RPC Server,监听 localhost:23333

 new Server
{
Services = { RpcStreamingService.BindService(new RpcStreamingServiceImpl()) }, // 绑定我们的实现
Ports = { new ServerPort("localhost", , ServerCredentials.Insecure) }
}.Start();
Console.ReadKey();

这样服务端就构建完成了。

编写客户端调用 RPC API

方便起见,我们先将 Main 函数改写为 async 函数。

 // 原来的 Main 函数
static void Main(string[] args) { ... }
// 改写后的 Main 函数
static async Task Main(string[] args) { ... }

另外,还需要:

 using System;
using System.IO;
using System.Threading.Tasks;
using Google.Protobuf;
using Grpc.Core;

然后我们在 Main 函数中添加调用代码:

 var channel = new Channel("localhost:23333", ChannelCredentials.Insecure); // 建立到 localhost:23333 的 channel
var client = new RpcStreamingService.RpcStreamingServiceClient(channel); // 建立 client
// 调用 RPC API
var result = client.GetStreamContent(new StreamRequest { FileName = "你想获取的文件路径" });
var iter = result.ResponseStream; // 拿到响应流
using (var fs = new FileStream("写获取的数据的文件路径", FileMode.Create)) // 新建一个文件流用于存放我们获取到数据
{
while (await iter.MoveNext()) // 迭代
{
iter.Current.Content.WriteTo(fs); // 将数据写入到文件流中
}
}

测试

dotnet run

会发现,我们想要获取的文件的数据被不断地写到我们指定的文件中,每次 1 Mb。在我的电脑上测试,内网的环境下传输速度大概 80~90 Mb/s,几乎跑满了我的千兆网卡,速度非常理想。

.NET Core + gRPC 实现数据串流 (Streaming)的更多相关文章

  1. 利用OData轻易实现串流数据的可视化

    OData(开放数据协议,Open Data Protocol)一直是我喜欢一种的标准(OASIS 标准),它基于RESTful协议提供了一种强大的查询和编辑数据的访问接口.虽然是微软推出的,不过在诞 ...

  2. Spark Streaming数据限流简述

      Spark Streaming对实时数据流进行分析处理,源源不断的从数据源接收数据切割成一个个时间间隔进行处理:   流处理与批处理有明显区别,批处理中的数据有明显的边界.数据规模已知:而流处理数 ...

  3. Asp.Net Core Grpc 入门实践

    Grpc简介 gRPC 是一种与语言无关的高性能远程过程调用 (RPC) 框架. 在 gRPC 中,客户端应用程序可以直接调用不同计算机上的服务器应用程序上的方法,就像它是本地对象一样,从而更轻松地创 ...

  4. ffmpeg利用libav库把yuv视频流转换为TS串流

    今天到月末了,才发我这个月的第一篇文章,因为这个月前三周一直在看ffmpeg的libavcodec和libavformat两个库源码.实验室要做一个“小传大”的软件,就是android手机或平板电脑的 ...

  5. VLC接收网络串流缓冲时间的计算 (转)

    原帖地址:http://blog.csdn.net/coroutines/article/details/7472743 VLC版本2.0.1 最近研究IP-STB音视频同步问题,发现方案自带的自动S ...

  6. NetCore服务虚拟化01(集群组件Sodao.Core.Grpc)

    一. 起始 去年.NetCore2.0的发布,公司决定新项目采用.NetCore开发,当作试验.但是问题在于当前公司内部使用的RPC服务为Thrift v0.9 + zookeeper版本,经过个性化 ...

  7. C++: I/O流详解(三)——串流

    一.串流 串流类是 ios 中的派生类 C++的串流对象可以连接string对象或字符串 串流提取数据时对字符串按变量类型解释:插入数据时把类型 数据转换成字符串 串流I/O具有格式化功能 例: #i ...

  8. ASP.NET Core gRPC 入门全家桶

    一. 说明 本全家桶现在只包含了入门级别的资料,实战资料更新中. 二.官方文档 gRPC in Asp.Net Core :官方文档 gRPC 官网:点我跳转 三.入门全家桶 正片: ASP.NET ...

  9. IO流总结---- 字节流 ,字符流, 序列化 ,数据操作流,打印流 , Properties 集合

    笔记内容: 什么是流 字节流 字符流 序列化 数据操作流(操作基本数据类型的流)DataInputStream 打印流 Properties 集合 什么是流: 流是个抽象的概念,是对输入输出设备的抽象 ...

随机推荐

  1. Halcon学习之七:改变图像的现实方式和大小

    change_format ( Image : ImagePart : Width, Height : ) 改变Image图像大小,而且ImagePart图像为灰度值图像. crop_domain ( ...

  2. ATL项目编译注册dll的时候报权限错误:error MSB8011: Failed to register output. Please try enabling Per-user Redirection or register the component from a command prompt with elevated permissions.

    atl工程在vs2013编译的时候会在编译成功之后去使用 regsvr32 去注册 生成的 .dll 偶尔在编译的时候会遇到下面的错误: error MSB8011: Failed to regist ...

  3. ffmpeg基本用法(转)

    FFmpeg FFmpeg 基本用法 本课要解决的问题 1.FFmpeg的转码流程是什么? 2.常见的视频格式包含哪些内容吗? 3.如何把这些内容从视频文件中抽取出来? 4.如何从一种格式转换为另一种 ...

  4. 从邮件原理来看 postfix和docecot

    好早好早以前计算机网络老师就教了说,邮件嘛,就三个协议smtp,imap,pop3. smtp 用来发邮件,imap,pop3用来收邮件.噢?是么.难道没有发现这句话有非常多的漏洞,根本就不能说清楚这 ...

  5. iOS布局之Auto Layout

    学习资源: <iOS6核心编程>自动布局部分 <iOS6范例经典>自动布局部分 Tutorial: iOS 6 Auto Layout versus Springs and S ...

  6. linux fg&bg

    [linux fg&bg] Linux 提供了 fg 和 bg 命令,让我们调度正在运行的任务. 假设你发现前台运行的一个程序需要很长的时间,但是需要干其他的事情,你就可以用 Ctrl-Z , ...

  7. JSP的原理、JSP的执行过程

    Jsp的本质是servlet, 通过response的printWriter返回,response的getOutputStream只能调用一次,返回流就不能返回页面刷新. JSP起源 在很多动态网页中 ...

  8. 蒟蒻LQL的博客

    这里是蒟蒻LQL的博客!!! 一枚水的不能再水的弱校ACMer···· 可能会在这写一些题解或者别的什么乱七八糟的··· 可能大概没什么人看,就当错题本好了o(* ̄▽ ̄*)ブ 因为太弱了难免有错误!发 ...

  9. Codeforces 667C DP

    题意:给你一个字符串,这个字符串的构造方法如下:先选择一个长度大于4的前缀,然后每次向字符串尾部添加一个长度为2或者长度为3的后缀,不能添加连续的相同的后缀,问可能的后缀有哪些?并按字典序输出去. 思 ...

  10. ubuntu18(笔记本) faster-rcnn实例程序运行

    luo@luo-ThinkPad-W540:TensorflowProject$ source activate flappbird (flappbird) luo@luo-ThinkPad-W540 ...