RPC基本理念:定义一个服务,指定其能够被远程调用的方法(包含参数和返回类型)。在服务端实现这个接口,并运行一个RPC服务器来处理客户端调用。在客户端拥有一个存根能够像服务端一样的方法。客户端应用可以像调用本地对象一样直接调用另一台不同的机器上服务端应用的方法,使得您能够更容易地创建分布式应用和服务。
gRPC是由谷歌开发的一种语言中立、平台中立、开源的RPC的高性能远程过程调用(RPC)框架。
说的白话一点,可以这么理解:现在有两台服务器A和B。部署在A服务器上的应用,想调用部署在B服务器上的另一个应用提供的方法,由于不在一个内存空间,不能直接调用,需要通过网络来达到调用的效果。
现在,我们在A服务的一个本地方法中封装调用B的逻辑,然后只需要在本地使用这个方法,就达到了调用B的效果。
gRPC消息使用Protobuf(一种高效的二进制消息格式)进行序列化。Protobuf在服务器和客户端上可以非常快速地序列化。Protobuf序列化产生的有效负载较小,这在移动应用等带宽有限的方案中很重要。
gRPC专为HTTP/2(HTTP的主要版本)而设计,与HTTP1.x相比,HTTP/2具有巨大性能优势:
HTTP/2不是gRPC独占的。许多请求类型(包括具有JSON的HTTPAPI)都可以使用HTTP/2,并受益于其性能改进。
具有JSON的HTTPAPI没有正式规范。开发人员为URL、HTTP谓词和响应代码的最佳格式争论不休。
HTTP/2为长期实时通信流提供基础。gRPC为通过HTTP/2进行流式传输提供一流支持。
gRPC服务支持所有流式传输组合:
一元RPC:客户端向服务器发送单个请求并返回单个响应,就像普通的函数调用一样,也就是最常见的客户端请求、服务端响应实现方式
服务器流式RPC:客户端向服务器发送请求并获取流以读取一系列消息。客户端从返回的流中读取,直到没有更多消息。gRPC保证单个RPC调用中的消息排序。
客户端流式RPC:其中客户端写入一系列消息并将它们发送到服务器,再次使用提供的流。一旦客户端完成写入消息,它等待服务器读取它们并返回其响应。gRPC再次保证单个RPC调用中的消息排序。
双向流式RPC:其中双方使用读写流发送一系列消息。这两个流独立运行,因此客户端和服务器可以按照他们喜欢的任何顺序进行读写:例如,服务器可以在写入响应之前等待接收所有客户端消息,或者它可以交替读取消息然后写入消息,或其他一些读取和写入的组合。保留每个流中消息的顺序。
当前无法通过浏览器直接调用gRPC服务。gRPC大量使用HTTP/2功能,且没有浏览器在Web请求中提供支持gRPC客户端所需的控制级别。例如,浏览器不允许调用方要求使用HTTP/2,也不提供对HTTP/2基础框架的访问。
ASP.NETCore上的gRPC提供两种兼容浏览器的解决方案:
HTTPAPI请求以文本形式发送,并且可进行人工读取和创建。
默认情况下,gRPC消息使用Protobuf进行编码。尽管Protobuf可以高效地发送和接收,但其二进制格式非人工可读取。Protobuf要求在.proto文件中指定消息接口描述来正确地反序列化。需要使用其他工具来分析网络上的Protobuf有效负载以及手动撰写请求。服务器反射和gRPC命令行工具等功能可帮助使用二进制Protobuf消息。此外,Protobuf消息支持与JSON之间的转换。内置的JSON转换提供在调试时将Protobuf消息与人工可读取格式互相转换的高效方法。
在以下方案中,建议使用其他框架取代gRPC:
1.创建服务端
在“创建新项目”对话框中,搜索gRPC。选择“ASP.NETCoregRPC服务”,新建名为GrpcGreeter的gRPC服务项目
GrpcGreeter项目文件:
解读一下greet.proto的文件syntax="proto3";//protobuf有2个版本,默认版本是proto2,如果需要proto3,则需要在非空非注释第一行使用syntax="proto3"标明版本。
GreeterService类中的代码就是服务的具体实现,我们的业务就写在这里
//配置服务端builder.Services.AddGrpc(options=>{options.EnableDetailedErrors=true;options.MaxReceiveMessageSize=2*1024*1024;//2MB//默认Null,消息大小不限制options.MaxSendMessageSize=5*1024*1024;//5MB//默认Null,消息大小不限制});//单个服务配置会覆盖全局选项//builder.Services.AddGrpc().AddServiceOptions
app.MapGrpcService
2.创建客户端
2.1我这里创建了一个.netcorewebapi的项目作为客户端,取名为GrpcClient。
2.2引入nuget包
Install-PackageGrpc.Net.ClientInstall-PackageGoogle.ProtobufInstall-PackageGrpc.Tools2.3创建Protos文件夹
2.4从gRPC服务端将Protos\greet.proto文件复制到gRPC客户端项目中的Protos文件夹。
2.5将客户端greet.proto文件中的命名空间更新为项目的命名空间,其他东西都不改变:
optioncsharp_namespace="GrpcClient";效果:
syntax="proto3";optioncsharp_namespace="GrpcClient";packagegreet;//Thegreetingservicedefinition.serviceGreeter{//SendsagreetingrpcSayHello(HelloRequest)returns(HelloReply);rpcSendMessage(SendMessageRequest)returns(SendMessageReply);}//Therequestmessagecontainingtheuser'sname.messageHelloRequest{stringname=1;}//Theresponsemessagecontainingthegreetings.messageHelloReply{stringmessage=1;}messageSendMessageRequest{stringmessage=1;}messageSendMessageReply{stringmessage=1;}
2.6编辑GrpcGreeterClient.csproj项目文件,添加具有引用greet.proto文件的
1.引入nuget包
Install-PackageMicrosoft.AspNetCore.Authentication.JwtBearer2.创建简易JWT帮助类
publicstaticclassJwtTicketHelper{publicstaticJwtSecurityTokenHandlerJwtTokenHandler=newJwtSecurityTokenHandler();publicstaticSymmetricSecurityKeySecurityKey=newSymmetricSecurityKey(Guid.NewGuid().ToByteArray());publicstaticstringCreateToken(stringname){if(string.IsNullOrEmpty(name)){thrownewInvalidOperationException("Nameisnotspecified.");}varclaims=new[]{newClaim(ClaimTypes.Name,name)};//这里可以自定义写入其他想要的claimvarcredentials=newSigningCredentials(SecurityKey,SecurityAlgorithms.HmacSha256);vartoken=newJwtSecurityToken("ExampleServer","ExampleClients",claims,expires:DateTime.Now.AddSeconds(60),signingCredentials:credentials);returnJwtTokenHandler.WriteToken(token);}}3.Program.cs配置:
app.MapGet("/generateJwtToken",context=>{returncontext.Response.WriteAsync(JwtTicketHelper.CreateToken(context.Request.Query["name"]));});注意:当然获取token这里也可以通过grpc的方式获取,下面两种方式都演示了
4.在Protos文件夹中创建新的proto文件演示,这里命名为ticketer.proto
注意:1.右键=》新建文件=》可以添加后缀为proto的“协议缓冲区文件”,但是一直提示“必须指定语言”的错误我就放弃了2.不要复制现有的greet.proto来创建文件,直接导致(GrpcGreeterClient\obj\Debug\[TARGET_FRAMEWORK]\Protos)生成不了相应的文件,直接报错,这个坑了我好久。不知道是不是bug会不会修复3.我这里是手动创建了一个文本文件,然后修改其后缀为proto
syntax="proto3";optioncsharp_namespace="GrpcTicketServer";import"google/protobuf/empty.proto";packageTicketer;serviceTicketer{rpcTestEmpty(google.protobuf.Empty)returns(google.protobuf.Empty);rpcSendMessage(Ticketer_SendMessageRequest)returns(Ticketer_SMessageReply);rpcGetToken(TokenRequest)returns(TokenReply);}messageTokenRequest{stringname=1;}messageTokenReply{stringtoken=1;}messageTicketer_SendMessageRequest{stringmessage=1;}messageTicketer_SMessageReply{stringmessage=1;}ticketer.proto5.在Services文件夹中创建实现类TicketerService。
[HttpGet("ticketer_sendmessage")]publicasyncTask
侦听器是一个gRPC概念,允许应用与传入或传出的gRPC调用进行交互。它们提供了一种方法来扩充请求处理管道,侦听器针对通道或服务进行配置,并针对每个gRPC调用自动执行。由于侦听器对用户的应用程序逻辑是透明的,因此它们是适用于常见情况(例如日志记录、监视、身份验证和验证)的极佳解决方案。
Interceptor方法为客户端重写以下项:
publicclassServerLoggerInterceptor:Interceptor{privatereadonlyILogger_logger;publicServerLoggerInterceptor(ILogger