What is gRPC
https://grpc.io/docs/guides/index.html
gRPC可以使用protocol buffers 作为其接口定义语言(IDL)及其底层信息交互格式。
Overview
在grpc中,一个client应用可以直接调用不同机器上的server应用的方法,好像它是一个局部对象,可以让你更加容易的构建分布式应用和服务。与许多RPC系统一样,gRPC基于定义服务的思想,指定可以使用其参数和返回类型远程调用的方法。在server端,server实现这个接口并返回一个gRPC server去处理client的调用。在client端,client有一个存根(stub)(在某些语言中被称为客户端),它提供与服务器相同的方法。
gRPC client 和 server可以在各种环境中相互运行和通信-从Google内部的服务器到你的桌面-并且可以使用任何gRPC支持的语言编写。比如,你可以轻易的使用Go、Python或Ruby构建gRPC client,使用java构建gRPC server。此外,最新的Google API将具有gRPC版本的界面,让你可以轻松地在应用中构建Google功能。
Working with Protocol Buffers
默认情况下,gRPC使用protocol buffers,这是Google用于序列化数据结构的成熟的开源机制(尽管它可以与其他数据格式(如JSON)一起使用)。这是它如何工作的快速介绍,如果你已经熟悉protocol buffers,可以随时跳到下一部分。
使用protocol buffers 的第一步是定义要在proto文件中序列化的数据的结构:这是一个扩展名为 .proto
的普通文本文件。protocol buffer数据被构造为messages,其中每条message都是包含一系列称为fields的 name-value 对的小型逻辑记录。如下是一个简单的例子:
message Person {
string name = 1;
int32 id = 2;
bool has_ponycopter = 3;
}
然后,一旦指定了数据结构,就可以使用protocol buffer的编译器protoc从你的proto定义生成首选语言的数据访问类。这为每个field提供了简单的访问器(如name()和set_name()),以及将整个机构序列化/解析为原始字节的方法——例如,如果你选择的语言是C++,则在上面的示例中运行编译器将生成一个名为Person的类。然后,你可以在应用程序中使用此类来填充,序列化和检索Person 这个 protocol buffer messages。
正如示例中,你可以在普通的proto文件中定义gRPC服务,并将RPC方法参数和返回类型指定为protocol buffer messages:
// The greeter 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;
}
gRPC还使用带有特殊gRPC插件的protoc来生成proto文件中的代码。然而,使用gRPC插件,你可以生成gRPC客户端和服务端代码,以及用于填充,序列化和检索message类型的常规protocol buffer代码。下面有一个更详细的例子。
Protocol buffer 版本
尽管protocol buffers已经开源了一段时间,但我们的例子使用了一种新的protocol buffers,被称为proto3,它具有略微简化的语法,一些有用的新功能,并支持更多语言。目前提供java,C++,Python,Objective-c,C#,Android Java,Ruby,及JavaScript,还有go,GitHub中都有相关库。
通常,尽管你可以使用proto2,但还是建议你使用proto3和gRPC支持的语言一起使用,以及避免proto2客户端和proto3服务端通信时的兼容性问题。
gRPC Concepts
https://grpc.io/docs/guides/concepts.html
本文介绍了一些关键的gPRC概念,概述了gRPC的体系结构和RPC生命周期。
Overview
Service definition
与许多RPC系统一样,gRPC基于定义服务的思想,指定可以使用其参数和返回类型远程调用的方法。默认情况下,gRPC使用protocol buffers作为接口定义语言(IDL)来描述服务接口和有效负载消息的结构,如果需要,可以使用其他替代方案。
service HelloService {
rpc SayHello (HelloRequest) returns (HelloResponse);
}
message HelloRequest {
string greeting = 1;
}
message HelloResponse {
string reply = 1;
}
gRPC允许你定义四种服务方法:
- 一元RPC,客户端向服务端发送单个请求并返回单个响应,就像正常的函数调用一样。
rpc SayHello(HelloRequest) returns (HelloResponse){
}
- 服务器流式RPC,客户端向服务器发送请求并获取流以读取消息序列。客户端从返回的流中读取,直到没有更多的消息。gRPC保证单个RPC调用中的消息排序。
rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse){
}
- 客户端流式RPC,客户端再次使用提供的流写入一系列消息并将其发送到服务器。一旦客户端写完消息,它就等待服务器读取它们并返回它的响应。gRPC再次保证在单个RPC调用中的消息排序。
rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse) {
}
- 双向流式RPC,其中双发使用读写流发送一系列消息。这两个流独立运行,因此客户端和服务器可以按照自己喜欢的顺序进行读写:例如,服务器可以在写入其响应之前等待接收所有客户端消息,或者它可以交替地读取消息然后写入消息,或者读取和写入地一些其他组合。保留每个流中地消息顺序。
rpc BidiHello(stream HelloRequest) returns (stream HelloResponse){
}
我们将在下面的RPC生命周期部分中更详细的介绍不同类型的RPC。
Using the API surface
从 .proto
文件中的服务定义开始,gRPC提供了生成客户端和服务端代码的protocol buffer编译器插件。gRPC用户通常在客户端调用这些API,并在服务端实现相应的API。
- 在服务端,服务器实现服声明的方法,并运行gRPC服务器来处理客户端调用。gRPC基础结构解码传入请求,执行服务方法并对服务响应进行编码。
- 在客户端,客户端有一个称为stub的本地对象(对于某些语言,首选术语是客户端),它实现与服务相同的方法。然后客户端可以在本地对象上调用这些方法,将调用的参数包装在适当的protocol buffer message类型中 - gRPC负责将请求发送到服务器并返回服务器的protocol buffer响应
同步 vs 异步
在响应从服务器到达之前阻塞的同步RPC调用,最接近RPC所期望的过程调用的抽象。另一方面,网络本质上是异步的,在许多情况下,能够在不阻塞当前线程的情况下启动RPC是有用的。
大多数语言的gRPC编程面都有同步和异步两种风格。
RPC life cycle
现在让我们仔细看看当gRPC客户端调用gRPC服务器方法时会发生什么。我们不会查看实现细节,你可以在特定语言页面中寻找相关信息。
Unary RPC
首先看最简单的RPC类型,客户端发送单个请求并返回单个响应。
- 客户端在stub/client对象上调用该方法后,将通知服务器已使用此调用的客户端元数据,方法名称和指定的截止时间来调用RPC。
- 然后,服务器可以立即发送回自己的初始元数据(必须在任何响应之前发送),或者等待客户端的请求message - 首先发生的是特定于应用程序的message。
- 一旦服务器具有客户端的请求message,它就会执行创建和填充其响应所需的任何工作。然后将响应与状态详细信息(状态代码和可选状态消息)以及可选的trailing元数据一起返回(如果成功)到客户端。
- 如果状态为OK,则客户端获取响应,从而完成客户端的调用。
Server streaming RPC
服务器流RPC类似于我们的简单示例,除了服务器在获取客户端的请求消息后发回响应流。在发回所有响应之后,服务器的状态详细信息(状态代码和可选状态消息)和可选的trailing元数据将被发送回服务器端完成。一旦客户端拥有所有服务器的响应,客户端就会完成。
Client streaming RPC
客户端流式RPC也类似于我们的简单示例,除了客户端向服务器发送请求流而不是单个请求。服务器发送回单个响应,通常但不一定在收到所有客户端请求后,附带其状态详情及可选的trailing元数据。
Bidirectional streaming RPC
在双向流式RPC中,再次调用由客户端调用方法及服务端接收的客户端元数据,方法名和截止时间发起。再一次服务器可以选择发回它的初始元数据或者等待客户端开始发起请求。
接下来发生什么取决于应用程序,因为客户端和服务器可以按任何顺序读写 - 流完全独立的运行。例如,服务器可以等到它在收到所有客户端的消息之后再写入其响应,或者服务器和客户端可以“ping-pong”:服务器获取一个请求,然后发回一个响应,然后客户端基于响应发送另一个请求,等等。
Deadlines/Timeouts
gRPC允许客户端指定在RPC以 DEADLINE_EXCEEDED 错误终止之前,等待一个期望时间。在服务器端,服务器可以查询特定RPC是否已超时,或者还有多少时间来完成RPC。
如何指定截止日期或超时因语言而异 - 如,并非所有语言都有默认的截止日期,某些语言API在截止日期(固定时间点)方面工作,而某些语言API在超时(持续时间)方面工作。
RPC termination
在gRPC中,客户端和服务器都对调用的成功进行独立和本地的确认,并且它们的结论可能不匹配。这意味着,比如你在客户端成功完成RPC(“我已发送所有响应!”),但在客户端失败(“响应在deadline后到达!”)。在客户端发送所有请求之前,服务器也可以决定完成。
Cancelling RPCs
客户端或服务器可以随时取消RPC。取消立即终止RPC,以便不再进行进一步的工作。它不是“undo”:取消之前所作的更改并不会回滚。
Metadata
元数据是关于特定RPC调用的信息(如身份验证详细信息),其形式为键值对列表,其中键是字符串,值通常是字符串(但可以是二进制数据)。元数据对gRPC本身是不透明的 - 它允许客户端提供与服务器调用相关的信息,反之亦然。
对元数据的访问取决于语言。
Channels
gRPC通道提供与指定主机和端口上的gRPC服务器的连接,并在创建客户端stub时使用。客户端可以指定通道参数来修改gRPC的默认行为,例如打开和关闭消息压缩。通道拥有状态,包含 connected 和 idle 。
gRPC如何处理关闭通道与语言有关。某些语言也允许查询通道状态。
Authentication
https://grpc.io/docs/guides/auth.html
本文概述gRPC身份验证,包括我们内置的支持身份验证机制,如何插入你自己的身份验证系统,以及如何在我们支持的语言中使用gRPC的示例。
Overview
gRPC旨在与各种身份验证机制配合使用,可以轻松安全的使用gRPC与其他系统进行通信。你可以使用我们支持的机制 - 带或不带Google令牌的身份验证SSL/TLS - 或者通过扩展我们提供的代码来插入自己的身份验证系统。
gRPC还提供了一个简单的身份验证API,允许你在创建频道或拨打调用的时候提供所有必要的身份验证信息作为 Credentials 。
Supported auth mechanisms
gRPC内置了一下身份验证机制:
- SSL/TLS:gRPC具有SSL/TLS集成,并促进使用SSL/TLS对服务器进行身份验证,并加密客户端和服务器之间交换的所有数据。客户端可以使用可选机制来提供相互身份验证的证书。
- google的基于token的身份验证:gRPC提供了一种通用机制,用于将基于元数据的凭证附加到请求和响应中。某些身份验证流程提供了在通过gRPC访问Google API时获取访问令牌(通常为OAuth2令牌)的其他支持:你可以在下面的代码示例中看到它的工作原理。通常,必须使用此机制以及通道上的SSL/TLS - Google不允许没有SSL/TLS的连接,并且大多数gRPC语言实现都不允许你在未加密的频道上发送凭据。
⚠ 注意: Google凭据只应用于连接Google服务。将Google发布的OAuth2令牌发送到非Google服务可能会导致此令牌被盗并用于冒充客户端到Google服务。
Authentication API
gRPC提供了一个基于Credentials对象的统一概念的简单身份验证API,可在创建整个gRPC频道或单个调用时使用。
Credential types
认证可以有两种类型:
- 频道认证,附加到频道上,如SSL 认证
- 调用认证,附加到调用上
你还可以将这些组合在CompositeChannelCredentials
中,如你可以指定频道的SSL详细信息以及在频道上进行的每个调用的调用凭据。CompositeChannelCredentials
将ChannelCredentials
和CallCredentials
相关联,以创建新的ChannelCredentials
。结果将发送给与组合的CallCredentials
相关联的认证数据与在通道上进行的每次调用。
例如,你可以从 SslCredentials
和 AccessTokenCredentials
创建 ChannelCredentials
。应用于Channel的结果将为此频道上的每个调用发送响应的访问令牌。
单个CallCredentials
也可以使用CompositeCallCredentials
组成。在调用中使用的CallCredentials
将触发与两个CallCredentials
关联的身份验证数据的发送。
Using client-side SSL/TLS
现在让我们看一下Credentials 如何使用我们支持的auth机制之一。这是最简单的身份验证方案,客户端只想验证服务器并加密所有数据。该示例使用的是C++,但所有语言的API都类似:你可以在下面的示例部分中了解如何在更多的语言中启用SSL/TLS。
// Create a default SSL ChannelCredentials object.
auto channel_creds = grpc::SslCredentials(grpc::SslCredentialsOptions());
// Create a channel using the credentials created in the previous step.
auto channel = grpc::CreateChannel(server_name, channel_creds);
// Create a stub on the channel.
std::unique_ptr<Greeter::Stub> stub(Greeter::NewStub(channel));
// Make actual RPC calls on the stub.
grpc::Status s = stub->sayHello(&context, *request, response);
对于高级用例,如修改根CA或使用客户端证书,可以在传递工厂方法的SslCredentialsOptions 参数中设置相应得选项。
Using Google token-based authentication
gRPC应用程序可以使用简单的API创建一个凭据,该凭据可用于在各种部署方案中与Google进行身份验证。同样我们使用C++,其他语言示例可以在示例部分中找到。
auto creds = grpc::GoogleDefaultCredentials();
// Create a channel, stub and make RPC calls (same as in the previous example)
auto channel = grpc::CreateChannel(server_name, creds);
std::unique_ptr<Greeter::Stub> stub(Greeter::NewStub(channel));
grpc::Status s = stub->sayHello(&context, *request, response);
此通道凭据对象适用于使用服务账户的应用程序及在GCE中运行的应用程序。在前一种情况下,服务账户的私钥是从环境变量GOOGLE_APPLICATION_CREDENTIALS
中指定的文件加载的。密钥用于生成附加到相应通道上的每个输出RPC的承载令牌。
对于在GCE中运行的应用程序,可以在VM设置期间配置默认服务账户和相应的OAuth2范围。 在运行时,此凭据处理与身份验证系统的通信以获取OAuth2访问令牌,并将它们附加到相应通道上的每个输出RPC。
Extending gRPC to support other authentication mechanisms
Credentials插件API允许开发人员插入他们自己的凭据类型。这包括:
MetadataCredentialsPlugin
抽象类,包含需要由开发人员创建的子类是新啊的纯虚拟GetMetadata
方法。MetadataCredentialsFromPlugin
函数,它从MetadataCredentialsPlugin
创建CallCredentials
。
以下是一个简单认证插件的示例,该插件在自定义标头中设置身份验证票据。
class MyCustomAuthenticator : public grpc::MetadataCredentialsPlugin {
public:
MyCustomAuthenticator(const grpc::string& ticket) : ticket_(ticket) {}
grpc::Status GetMetadata(
grpc::string_ref service_url, grpc::string_ref method_name,
const grpc::AuthContext& channel_auth_context,
std::multimap<grpc::string, grpc::string>* metadata) override {
metadata->insert(std::make_pair("x-custom-auth-ticket", ticket_));
return grpc::Status::OK;
}
private:
grpc::string ticket_;
};
auto call_creds = grpc::MetadataCredentialsFromPlugin(
std::unique_ptr<grpc::MetadataCredentialsPlugin>(
new MyCustomAuthenticator("super-secret-ticket")));
通过在核心级插入gRPC凭证实现,可以实现更深入的集成。gRPC内部还允许使用其他加密机制切换SSL/TLS。
一些例子,这里就不贴了,自己去看:https://grpc.io/docs/guides/auth.html
Java
基本情况 - 无加密或身份验证
ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 50051)
.usePlaintext(true)
.build();
GreeterGrpc.GreeterStub stub = GreeterGrpc.newStub(channel);
使用服务器身份验证SSL/TLS
在java中,我们建议你在使用gRPC over TLS时使用OpenSSL。你可以在gRPC java安全性文档中找到有关安装和使用OpenSSL以及Android和非Android Java所需的其他库的详细信息。
要在服务器上启用TLS,需要以PEM格式指定证书链和私钥。这样的私钥不应该使用密码。链中的证书顺序很重要:更具体的说,顶部的证书必须是主机CA,而底部的证书必须是根CA。标准TLS端口是443,但我们使用下面的8443以避免需要操作系统的额外权限。
Server server = ServerBuilder.forPort(8443)
// Enable TLS
.useTransportSecurity(certChainFile, privateKeyFile)
.addService(TestServiceGrpc.bindService(serviceImplementation))
.build();
server.start();
如果客户端不知道颁发证书机构,则 NettyChannelBuilder
或者 OkHttpChannelBuilder
分别配置SslContext
或 SSLSocketFactory
属性。
在客户端,使用SSL/TLS的服务器身份验证如下所示:
// With server authentication SSL/TLS
ManagedChannel channel = ManagedChannelBuilder.forAddress("myservice.example.com", 443)
.build();
GreeterGrpc.GreeterStub stub = GreeterGrpc.newStub(channel);
// With server authentication SSL/TLS; custom CA root certificates; not on Android
ManagedChannel channel = NettyChannelBuilder.forAddress("myservice.example.com", 443)
.sslContext(GrpcSslContexts.forClient().trustManager(new File("roots.pem")).build())
.build();
GreeterGrpc.GreeterStub stub = GreeterGrpc.newStub(channel);
Google认证
以下代码段显示了如何使用带有服务账户的gRPC调用Google Cloud PubSub API。凭证从存储在众所周知的位置的密钥加载,或者通过检测应用程序在可以自动提供一个的环境中运行。虽然此示例特定于Google及其服务,但其他服务提供商可以遵循类似的模式。
GoogleCredentials creds = GoogleCredentials.getApplicationDefault();
ManagedChannel channel = ManagedChannelBuilder.forTarget("greeter.googleapis.com")
.build();
GreeterGrpc.GreeterStub stub = GreeterGrpc.newStub(channel)
.withCallCredentials(MoreCallCredentials.from(creds));
Error Handing
https://grpc.io/docs/guides/error.html
本文描述了gRPC如何处理错误,包括gRPC的内置错误代码。可以在此处找到不同语言的示例代码。
Error model
正如你在我们的概念文档和示例中所看到的,当gRPC调用成功完成时,服务器会向客户端返回OK状态(取决于语言,OK状态可能会或者可能不会直接在你的代码中使用)。但如果不成功会发生什么?
如果发生错误,gRPC会返回其错误状态代码之一,并带有可选的字符串错误消息,该消息提供有关所发生情况的更多详细信息。所有支持的语言中的gRPC客户端都可以使用错误信息。
Error status codes
gRPC在各种情况下引发错误,从网络故障到未经认证的连接,每个连接都与特定的状态代码相关联。所有gRPC语言都支持以下错误状态代码。
General errors
Case | Status code |
---|---|
Client application cancelled the request | GRPC_STATUS_CANCELLED |
Deadline expired before server returned status | GRPC_STATUS_DEADLINE_EXCEEDED |
Method not found on server | GRPC_STATUS_UNIMPLEMENTED |
Server shutting down | GRPC_STATUS_CANCELLED |
Server threw an exception (or did something other than returning a status code to terminate the RPC) | GRPC_STATUS_UNKNOWN |
Network failures
Case | Status code |
---|---|
No data transmitted before deadline expires. Also applies to cases where some data is transmitted and no other failures are detected before the deadline expires | GRPC_STATUS_DEADLINE_EXCEEDED |
Some data transmitted (for example, the request metadata has been written to the TCP connection) before the connection breaks | GRPC_STATUS_UNAVAILABLE |
Protocol errors
Case | Status code |
---|---|
Could not decompress but compression algorithm supported | GRPC_STATUS_INTERNAL |
Compression mechanism used by client not supported by the server | GRPC_STATUS_UNIMPLEMENTED |
Flow-control resource limits reached | GRPC_STATUS_RESOURCE_EXHAUSTED |
Flow-control protocol violation | GRPC_STATUS_INTERNAL |
Error parsing returned status | GRPC_STATUS_UNKNOWN |
Unauthenticated: credentials failed to get metadata | GRPC_STATUS_UNAUTHENTICATED |
Invalid host set in authority metadata | GRPC_STATUS_UNAUTHENTICATED |
Error parsing response protocol buffer | GRPC_STATUS_INTERNAL |
Error parsing request protocol buffer | GRPC_STATUS_INTERNAL |