什么是gRPC
官方的定义:
gRPC is a modern open source high performance RPC framework that can run in any environment. It can efficiently connect services in and across data centers with pluggable support for load balancing, tracing, health checking and authentication. It is also applicable in last mile of distributed computing to connect devices, mobile applications and browsers to backend services.
gRPC是一种现代化开源的高性能RPC框架,能够运行于任意环境之中。它可以高效地在服务和数据中心内部与其间建立连接,并且可支持负载均衡,追踪,健康检测与认证功能。同时它也可用于分布式计算的“最后一公里”,连接设备,移动应用和浏览器到后端服务。
维基的定义:
gRPC (gRPC Remote Procedure Calls) is an open source remote procedure call (RPC) system initially developed at Google. It uses HTTP/2 for transport, Protocol Buffers as the interface description language, and provides features such as authentication, bidirectional streaming and flow control, blocking or nonblocking bindings, and cancellation and timeouts. It generates cross-platform client and server bindings for many languages. Most common usage scenarios include connecting services in microservices style architecture and connect mobile devices, browser clients to backend services.
gRPC(gRPC远程过程调用)是一种开源的远程过程调用系统,最初由谷歌进行开发。它使用HTTP/2作为传输协议。Protocol Buffer被用于接口描述语言,提供了诸如认证,双向流控制,阻塞或非阻塞绑定,取消与超时功能。它为多种语言生成跨平台的客户端与服务端绑定。最通用的使用场景包括在微服务样式架构中连接服务,以及连接移动设备,浏览器客户端到后端服务。
什么是RPC
从上面的解释里不难看出,要对gRPC有更直观的理解,首先需要明白RPC的定义。
继续看下维基上的内容:
In distributed computing, a remote procedure call (RPC) is when a computer program causes a procedure (subroutine) to execute in a different address space (commonly on another computer on a shared network), which is coded as if it were a normal (local) procedure call, without the programmer explicitly coding the details for the remote interaction. That is, the programmer writes essentially the same code whether the subroutine is local to the executing program, or remote. This is a form of client–server interaction (caller is client, executor is server), typically implemented via a request–response message-passing system. In the object-oriented programming paradigm, RPC calls are represented by remote method invocation (RMI). The RPC model implies a level of location transparency, namely that calling procedures is largely the same whether it is local or remote, but usually they are not identical, so local calls can be distinguished from remote calls. Remote calls are usually orders of magnitude slower and less reliable than local calls, so distinguishing them is important.
在分布式计算里,当计算机程序要运行一段代码,远程过程调用(RPC)会在不同的内存地址(一般是在网络里的不同计算机)上执行它。其无需程序员显示地对它的远程交互进行具体编码,编写代码的方式就如同一段普通的本地过程调用一般。即是,程序员所需写的代码是相同的,无论其调用的过程是在本地还是远程。这是一种客户端-服务端交互的形式(调用方是客户端,执行方是服务端),典型的实现方式是通过一种请求-响应的传递消息系统。在面向对象程序范式里,RPC调用被表示为远程方式调用(RMI)。RPC模型意味着一定程度的本地透明性,本地或是远程的调用过程大致而言是等同的,但又不是完全一样。远程调用通常比本地调用慢上几个数量级,同时也更加不可靠。
Remoting,Web Service,WCF
如果对RPC的概念还是理解不了的话,却曾使用过或者了解过诸如Remoting,Web Service或者WCF等技术的话,那么其实在这方面已经入门了。
Protocol buffers
Protocol buffers是谷歌开发的一套无关开发语言,无关平台的用于序列化结构数据的可扩展机制。它的用途与XML,JSON相同,但比它们更小,更快,更简洁。
作为gRPC中默认的接口定义语言,其既可以用于定义服务接口,也可以定义所必要的数据结构。
service HelloService {
rpc SayHello (HelloRequest) returns (HelloResponse);
}
message HelloRequest {
string greeting = 1;
}
message HelloResponse {
string reply = 1;
}
Protocol buffers的代码需要保存在扩展名为.proto
的文件之中。
服务类型
gRPC支持四种服务类型:
- 一元式RPC
- 服务端流式RPC
- 客户端流式RPC
- 双向流式RPC
一元式RPC,如同普通的方法调用一般,客户端的单一请求,至服务端后,返回唯一的响应。
rpc SayHello(HelloRequest) returns (HelloResponse){
}
服务端流式RPC,客户端发送一请求至服务端后,服务端返回一串消息数据流,客户端持续读取直到没有更多的消息。gRPC会确保消息的传输顺序。
rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse){
}
客户端流式RPC,客户端对一数据流写入一串信息后发送到服务端,并等待服务端返回响应。gRPC会确保消息的传输顺序。
rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse) {
}
双向流式RPC,客户端与服务端使用两个数据流,其间互无影响,可以同时进行数据传递。
rpc BidiHello(stream HelloRequest) returns (stream HelloResponse){
}
gRPC API
光有proto文件,并不能直接使用,还需gRPC中所提供的API将这些文件编译成不同程序语言的服务端与客户端代码。
在服务端,实现服务所定义的方法,并运行gRPC服务程序以处理客户端调用。gRPC架构负责解码传入的请求,执行服务的方法,最后对服务的响应进行编码。
在客户端,会有一个名为存根(stub)的本地对象,实现与服务端相同的方法。这样只需调用这个本地对象的方法,传入合适的protocol buffers消息类型的参数,gRPC负责将请求发送至服务端,再返回相应的结果。
如果使用过诸如WCF这样的PRC技术,对这一过程应该是十分熟悉了。
感到陌生的大概是gRPC中将proto文件编译成代码的插件工具,例如Grpc.Tools
。
所以还是举个具体的例子以便理解。
.NET中的gPRC
首先建立两个Console工程。
dotnet new console -o gRPCServer
dotnet new console -o gRPCClient
接着,在此两个工程同级目录下新建一个名为protos的文件夹。
如此,目录下的文件夹应该有如下几个:
- gRPCServer
- gRPCClient
- protos
protos文件夹下新加一个greet.proto文件。
syntax = "proto3";
option csharp_namespace = "GrpcGreeter";
package Greet;
// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply);
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings.
message HelloReply {
string message = 1;
}
然后,进入gRPCServer文件夹,添加相关的类库包。
dotnet add package Grpc
dotnet add package Grpc.Tools
dotnet add package Google.Protobuf
并在csproj文件中加入下列配置:
<ItemGroup>
<Protobuf Include="../protos/*.proto" OutputDir="protos" CompileOutputs="false" GrpcServices="Service" />
</ItemGroup>
运行dotnet build
,可以看到在gRPCServer工程下新生成一个protos文件夹,以及Greet.cs与GreetGrpc.cs文件。
在Program类中加入Greeter的实现类和相关接口方法,并创建服务端启动程序。
class Program
{
const int Port = 51234;
static void Main(string[] args)
{
var server = new Server
{
Services = { Greeter.BindService(new GreeterImpl()) },
Ports = { new ServerPort("localhost", Port, ServerCredentials.Insecure) }
};
server.Start();
Console.WriteLine("Greeter server listening on port " + Port);
Console.WriteLine("Press any key to stop the server...");
Console.ReadKey();
server.ShutdownAsync().Wait();
}
}
class GreeterImpl : Greeter.GreeterBase
{
// Server side handler of the SayHello RPC
public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context)
{
return Task.FromResult(new HelloReply { Message = "Hello " + request.Name });
}
}
通过dotnet run
命令启动此服务端。
之后再进入gRPCClient文件夹,加入同样的类库包。
在csproj文件中加入类似的配置,注意GrpcServices属性改为了Client:
<ItemGroup>
<Protobuf Include="../protos/*.proto" OutputDir="protos" CompileOutputs="false" GrpcServices="Client" />
</ItemGroup>
运行dotnet build
,同样生成protos文件夹及两个proto文件。
Program类中加入客户端调用代码。
static void Main(string[] args)
{
var channel = new Channel("127.0.0.1:51234", ChannelCredentials.Insecure);
var client = new Greeter.GreeterClient(channel);
var reply = client.SayHello(new HelloRequest { Name = "Ken" });
Console.WriteLine(reply.Message);
channel.ShutdownAsync().Wait();
Console.ReadKey();
}
运行客户端后,应该就可以看到调用服务端方法后返回的结果。
.NET Core 3.0
在最新的.NET Core 3.0中,加入了对gRPC更多的支持。当创建一个Web项目时,选择ASP.NET Core 3.0,可以发现新增了gRPC Service项目模板。
从建立的项目代码中可以看出其不再是普通的Web应用,而是完全变成了gRPC的服务端。
public void ConfigureServices(IServiceCollection services)
{
services.AddGrpc();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseEndpoints(endpoints =>
{
// Communication with gRPC endpoints must be made through a gRPC client.
// To learn how to create a client, visit: https://go.microsoft.com/fwlink/?linkid=2086909
endpoints.MapGrpcService<GreeterService>();
});
}