系列章節 GRPC與.net core GRPC截止時間與元數據 GRPC與netcore Identity GRPC與netcore IdentityServer4 概述 GRPC的數據交互模式有: 1.單項RPC,最簡單的數據交換方式,客戶端發出單個請求,收到單個響應 2.服務端流式RPC,是在 ...
系列章節
概述
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的幾種數據交互分享完畢