GRPC与.net core
系列章节
概述
grpc的数据交互模式有:
1.单项rpc,最简单的数据交换方式,客户端发出单个请求,收到单个响应
2.服务端流式rpc,是在服务端收到客户端的请求之后,返回一个应答流,客户端收到流之后处理。
3.客户端流式rpc,与单项类似,但客户端发送的是流式rpc
4.双向流式rpc,调用由客户端调用方法来初始化,而服务端则接收到客户端的元数据,方法名和截止时间。服务端可以选择发送回它的初始元数据或等待客户端发送请求。下一步怎样发展取决于应用,因为客户端和服务端能在任意顺序上读写 - 这些流的操作是完全独立的。例如服务端可以一直等直到它接收到所有客户端的消息才写应答,或者服务端和客户端可以像"乒乓球"一样:服务端后得到一个请求就回送一个应答,接着客户端根据应答来发送另一个请求,以此类推。
单项rpc较简单不做示例了。
首先在vs2019中net core3.0中新建grpc项目。然后定义响应的proto文件,根据proto文件生成响应的服务端与客户端代码。
1.服务端流式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(); } } }
文件生成成功
2.客户端流式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(); } } }
3.双向流式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的几种数据交互分享完毕
下一篇: 基于JWT的Token登录认证