系列章節
概述
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的幾種資料互動分享完畢