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: