查看原文
其他

GRPC与.NET Core

DotNet 2021-09-23

(给DotNet加星标,提升.Net技能


转自:老六代码
cnblogs.com/chenyishi/p/11143375.html

概述


GRPC的数据交互模式有:


1、单项RPC,最简单的数据交换方式,客户端发出单个请求,收到单个响应


2、服务端流式RPC,是在服务端收到客户端的请求之后,返回一个应答流,客户端收到流之后处理。


3、客户端流式RPC,与单项类似,但客户端发送的是流式RPC


4、双向流式RPC,调用由客户端调用方法来初始化,而服务端则接收到客户端的元数据,方法名和截止时间。服务端可以选择发送回它的初始元数据或等待客户端发送请求。下一步怎样发展取决于应用,因为客户端和服务端能在任意顺序上读写 - 这些流的操作是完全独立的。例如服务端可以一直等直到它接收到所有客户端的消息才写应答,或者服务端和客户端可以像"乒乓球"一样:服务端后得到一个请求就回送一个应答,接着客户端根据应答来发送另一个请求,以此类推。


单项RPC较简单不做示例了。


首先在VS 2019中.NET Core 3.0中新建GRPC项目。然后定义响应的proto文件,根据proto文件生成响应的服务端与客户端代码。


一、服务端流式RPC


1、定义 protofile


syntax = "proto3";

option csharp_namespace = "GrpcGreeter";

package Greet;

// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}
rpc GetStreamContent (StreamRequest) returns (stream StreamContent) {}
}

// The request message containing the user's name.
message HelloRequest {
string name = 1;
}

// The response message containing the greetings.
message HelloReply {
string message = 1;
}

message StreamRequest {
string fileName = 1;
}
message StreamContent {
bytes content = 1;
}


2、实现服务端Service


重新生成项目,然后实现GetStreamContent,简单的读取文件内容,并将内容返回给Client


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


3、实现Client


新建一个netcore 3.0的Console项目,并引入Nuget包



Install-Package Grpc.Net.Client -Version 0.1.22-pre1
Install-Package Google.Protobuf -Version 3.8.0
Install-Package Grpc.Tools -Version 1.22.0


编辑项目文件,修改如下节点


<Protobuf Include="Protos\greet.proto" GrpcServices="Client" />


重新生成项目,Client端主要实现发送请求,请求是一个服务器端的文件路径。然后实现接收服务端的流,并保存到Client本地。


using Grpc.Net.Client;
using GrpcGreeter;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;

namespace GrpcGreeterClient
{
class Program
{
static async System.Threading.Tasks.Task Main(string[] args)
{
AppContext.SetSwitch( "System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport",
true);
var httpClient = new HttpClient();
// The port number(50051) must match the port of the gRPC server.
httpClient.BaseAddress = new Uri("http://localhost:50051");
var client = GrpcClient.Create<Greeter.GreeterClient>(httpClient);
//
var reply = await client.SayHelloAsync(
new HelloRequest { Name = "GreeterClient" });
Console.WriteLine("Greeting: " + reply.Message);
Console.ReadKey();
//
var result = client.GetStreamContent(new StreamRequest { FileName = @"D:\Docs.zip" }); //发送请求
var iter = result.ResponseStream;
using (var fs = new FileStream(@"D:\Docs2.zip", FileMode.Create)) // 新建一个文件流用于存放我们获取到数据
{
while (await iter.MoveNext()) // 迭代
{
iter.Current.Content.WriteTo(fs); // 将数据写入到文件流中
}
}
Console.ReadKey();
}
}
}


文件生成成功



二、客户端流式RPC


1、定义 protofile


syntax = "proto3";
option csharp_namespace = "GRPC.TEST";
package Greet;

// The greeting service definition.
service Greeter {
rpc getResult (stream Value) returns (Result) {}
}

//定义Value消息类型,用于客户端消息
message Value {
int32 value = 1;
}
//定义Result消息类型,包含总和,数字数量和平均值,用于服务端消息返回
message Result {
int32 sum = 1;
int32 cnt = 2;
double avg = 3;
}


2、实现服务端Service


重新生成项目,并实现如下


using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Google.Protobuf;
using Grpc.Core;

namespace GRPC.TEST
{
public class GreeterService : Greeter.GreeterBase
{
public override async Task<Result> getResult(IAsyncStreamReader<Value> requestStream, ServerCallContext context)
{
while (await requestStream.MoveNext())
{
var point = requestStream.Current;
}
return new Result { Sum = 1 };
}
}
}


3、实现Client


新建一个netcore 3.0的Console项目,并引入Nuget包,安装nuget包与其他操作同上一个例子,实现代码如下


using Grpc.Net.Client;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;

namespace GRPC.TEST.CLIENT
{
class Program
{
static async System.Threading.Tasks.Task Main(string[] args)
{
AppContext.SetSwitch( "System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport",
true);
var httpClient = new HttpClient();
// The port number(50051) must match the port of the gRPC server.
httpClient.BaseAddress = new Uri("http://localhost:50051");
var client = GrpcClient.Create<Greeter.GreeterClient>(httpClient);
using (var call = client.getResult())
{
await call.RequestStream.WriteAsync(new Value { Value_ = 1 });
await call.RequestStream.CompleteAsync();
var response = await call.ResponseAsync;
}
Console.ReadKey();
}
}
}


三、双向流式RPC


1、定义proto


syntax = "proto3";
option csharp_namespace = "GRPC.TEST";
package Greet;

// The greeting service definition.
service Greeter {
rpc getResult (stream Value) returns (stream Result) {}
}


//定义Value消息类型,用于客户端消息
message Value {
int32 value = 1;
}
//定义Result消息类型,包含总和,数字数量和平均值,用于服务端消息返回
message Result {
int32 sum = 1;
int32 cnt = 2;
double avg = 3;
}


2、服务端实现


重新生成项目,并实现如下


public override async Task getResult(IAsyncStreamReader<Value> requestStream, IServerStreamWriter<Result> responseStream, ServerCallContext context)
{
while (await requestStream.MoveNext())
{
var note = requestStream.Current;
await responseStream.WriteAsync(new Result { Sum = 100 });
}
}


3、客户端代码


新建一个netcore 3.0的Console项目,并引入Nuget包,安装nuget包与其他操作同上一个例子,实现代码如下


using Grpc.Net.Client;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Threading.Tasks;
namespace GRPC.TEST.CLIENT
{
class Program
{
static async System.Threading.Tasks.Task Main(string[] args)
{
AppContext.SetSwitch(
"System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport",

true);
var httpClient = new HttpClient();
// The port number(50051) must match the port of the gRPC server.
httpClient.BaseAddress = new Uri("http://localhost:50051");
var client = GrpcClient.Create<Greeter.GreeterClient>(httpClient);
using (var call = client.getResult())
{
var responseReaderTask = Task.Run(async () =>
{
while (await call.ResponseStream.MoveNext())
{
var note = call.ResponseStream.Current;
Console.WriteLine("Received " + note);
}
});
await call.RequestStream.WriteAsync(new Value { Value_ = 12 });
await call.RequestStream.CompleteAsync();
await responseReaderTask;
}
Console.ReadKey();
}
}
}


至此,GRPC的几种数据交互分享完毕


推荐阅读

(点击标题可跳转阅读)

ASP.NET Core 3.0 gRPC框架

.NET Core微服务之Grpc初体验

C# 8.0两个有趣的新特性以及gRPC


看完本文有收获?请转发分享给更多人

关注「DotNet」加星标,提升.Net技能 

好文章,我在看❤️

: . Video Mini Program Like ,轻点两下取消赞 Wow ,轻点两下取消在看

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存