gRPC 是一種與語言無關的高性能遠程過程調用 (RPC) 框架。
gRPC 的主要優點是:
- 現代高性能輕量級 RPC 框架。
- 協定優先 API 開發,默認使用協議緩沖區,允許與語言無關的實現。
- 可用於多種語言的工具,以生成強類型服務器和客戶端。
- 支持客戶端、服務器和雙向流式處理調用。
- 使用 Protobuf 二進制序列化減少對網絡的使用。
這些優點使 gRPC 適用於:
- 效率至關重要的輕量級微服務。
- 需要多種語言用於開發的 Polyglot 系統。
- 需要處理流式處理請求或響應的點對點實時服務。
以上來自微軟的文檔:https://docs.microsoft.com/zh-cn/aspnet/core/grpc/index?view=aspnetcore-3.0
個人理解:
- gRPC采用HTTP/2協議,二進制傳輸,相比json,xml速度更快,更節省流量。
- 支持流,只需要建立一次連接,適合服務間通訊。
- 規范的接口標准。
- 跨語言。
補充:
Restful是一種架構風格,關注的是資源。
通過每次http請求把資源拿過來,但資源怎么用是客戶端的事情。
gRpc是rpc的一個實現框架,因此關注其中的rpc遠程過程調用。
意思是在客戶端調用服務器方法就像調用本地方法一樣。如果這樣做,那么服務器就必須要有相應的處理的方法(參數和返回值)。
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:
github地址:https://github.com/yeqifeng2288/GrpcDemo