gRPC 是一種與語言無關的高性能遠程過程調用 (RPC) 框架。git
gRPC 的主要優勢是:github
這些優勢使 gRPC 適用於:web
以上來自微軟的文檔:https://docs.microsoft.com/zh-cn/aspnet/core/grpc/index?view=aspnetcore-3.0json
我的理解:api
補充:服務器
Restful是一種架構風格,關注的是資源。網絡
經過每次http請求把資源拿過來,但資源怎麼用是客戶端的事情。架構
gRpc是rpc的一個實現框架,所以關注其中的rpc遠程過程調用。框架
意思是在客戶端調用服務器方法就像調用本地方法同樣。若是這樣作,那麼服務器就必需要有相應的處理的方法(參數和返回值)。dom
grpc在proto文件中定義了方法名和返回值,在各類語言中,咱們只須要在服務器和客戶端實現相應的方法便可。
如下Demo主要體現了服務端與客戶端以流式RPC的方式,並對比webapi的方式。
*必須使用http/2,所以須要在服務器上監聽端口設置。
//支持無tls的http/2。 webBuilder.ConfigureKestrel(options => { options.ListenLocalhost(5000, o => o.Protocols = Microsoft.AspNetCore.Server.Kestrel.Core.HttpProtocols.Http2); });
*客戶端須要設置
AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
C#自動生成代碼,客戶端須要從nuget安裝:
Google.Protobuf
Grpc.Core
Grpc.Net.ClientFactory
Grpc.Tools
項目文件:
<ItemGroup>
<Protobuf Include="Protos\Duplicate.proto" GrpcServices="Client">
</Protobuf>
</ItemGroup>
服務的須要
Grpc.Tools
Grpc.AspNetCore.Server
Grpc.AspNetCore
項目文件:
<ItemGroup>
<Protobuf Include="Protos/Duplicate.proto" GrpcServices="Server" />
</ItemGroup>
該Demo模擬了一個判重的服務器和客戶端。
public interface IDuplicate { /// <summary> /// 將標籤進入判重。 /// </summary> /// <param name="tag">標籤。</param> /// <returns>保存成功後將返回一個值。</returns> bool EntryDuplicate(string tag); /// <summary> /// 判斷標籤是否已經存在。 /// </summary> /// <param name="tag">標籤。</param> /// <returns>若是標籤存在則返回true。</returns> bool DuplicateCheck(string tag); /// <summary> /// 刪除一條標籤。 /// </summary> /// <param name="tag">標籤。</param> /// <returns>返回結果。</returns> bool RemoveItem(string tag); }
Proto配置:
syntax = "proto3"; // 命名空間。 option csharp_namespace = "GrpcServer.Protos"; package Duplicate; service Duplicater{ // 進隊列接口。 rpc EntryDuplicate(stream EntryRequset) returns (stream EntryResponse); // 判重接口。 rpc DuplicateCheck(stream DuplicateCheckRequset) returns (stream DuplicateCheckResponse); } // 進隊列請求。 message EntryRequset{ // tag=1,表示在傳輸過程當中,此數據的名字就是1。 string tag=1; } // 進隊後響應。 message EntryResponse{ bool result=1; string msg=2; } // 判重請求。 message DuplicateCheckRequset{ string tag=1; } // 判重後響應。 message DuplicateCheckResponse{ bool result=1; }
Demo中主要實現了入判重的方法。
/// <summary> /// 入判重。 /// </summary> /// <param name="requestStream">請求流。</param> /// <param name="responseStream">響應流。</param> /// <param name="context">上下文。</param> /// <returns></returns> public override async Task EntryDuplicate(IAsyncStreamReader<EntryRequset> requestStream, IServerStreamWriter<EntryResponse> responseStream, ServerCallContext context) { while (await requestStream.MoveNext()) { var result = _memoryDuplicate.EntryDuplicate(requestStream.Current.Tag); var msg = string.Empty; if (result) msg = $"{requestStream.Current.Tag} 入判重成功。"; else msg = $"{requestStream.Current.Tag} 入判重失敗,已有重複的數據"; _logger.LogInformation(msg); await responseStream.WriteAsync(new EntryResponse { Result = result, Msg = msg }); } _logger.LogInformation("本次請求已完成"); }
由客戶端告知流傳輸結束,而後釋放鏈接:
var token = new CancellationToken(); var response = Task.Run(async () => { while (await entry.ResponseStream.MoveNext(token)) { if (entry.ResponseStream.Current.Result) Console.WriteLine($"{entry.ResponseStream.Current.Msg}"); else Console.WriteLine($"{entry.ResponseStream.Current.Msg}入判重失敗。"); } }); for (int i = 0; i < length; i++) { SpinWait.SpinUntil(() => false, 200); var msg = random.Next(0, 2000).ToString(); await entry.RequestStream.WriteAsync(new EntryRequset { Tag = msg }); } Console.WriteLine("等待釋放連接。"); await entry.RequestStream.CompleteAsync(); entry.Dispose(); Console.WriteLine("完成");
Grpc:
WebApi: