gRPC Connectivity Semantics and API
原文:https://github.com/grpc/grpc/blob/master/doc/connectivity-semantics-and-api.md
本文主要描述 gRPC 通道的连接语义以及对 RPC 的相应影响,然后再探讨下 API。
States of Connectivity
gRPC Channels 提供了一种 clients 与 servers 交互的抽象。客户端的 channel 对象可以通过使用一个 DNS 名字多一点的信息就可以构建出来。Channels 封装了一系列功能,包括:名称解析、建立TCP连接(包括 retries 和 backoff)和 TLS 握手。
Channels 还可以处理已建立连接上的错误并重新连接,或者在 HTTP/2 GO_AWAY 的情况下,重新解析名称并重新连接。
为了对使用 gRPC API的用户(即应用程序代码)隐藏所有此活动的详细信息,同时暴露有关 channel 状态的有意义信息,我们使用了如下定义的五个状态表述的状态机:
CONNECTING:
标识该 channel 正在尝试建立连接,并且正在等待在 名称解析、TCP 连接建立 或 TLS 握手 中涉及的其中一个步骤上取得进展。它可以被用作创建 channel 时的初始状态。
READY:
标识该 channel 已经通过 TLS握手(或等效)和协议级(HTTP/2等)握手 成功建立了连接,并且所有后续的通信尝试都已成功(或者在没有任何已知故障的情况下等待)。
TRANSIENT_FAILURE:
标识该 channel 出现了一些瞬时故障(例如,TCP 3次握手超时 或 套接字错误)。处于此状态的 channel 最终将切换到 CONNECTING 状态并尝试再次建立连接。
由于重试是通过指数退避(exponential backoff)完成的,因此,连接失败的 channel 在刚开始时在此状态下花费很少的时间,但是随着重复尝试并失败的次数增加,channel 将在此状态下花费越来越多的时间。对于许多非致命故障(例如,由于服务器尚不可用而导致 TCP 连接尝试超时),channel 可能在该状态下花费越来越多的大量时间。
IDLE:
这个状态标识由于缺少新的(new)或未决(pending)的RPC,channel 甚至没有尝试创建连接。新的 RPC 可能会在这个状态被创建。任何在 channel 上启动 RPC 的尝试都会将通道从此状态推送到 CONNECTING 状态。
如果 channel 上已经在指定的 IDLE_TIMEOUT 时间内没有 RPC 活动,即在此期间没有新的(new)或挂起(pending)的(或活跃的) RPC,则 READY 或 CONNECTING 状态的 channel 将转换到 IDLE 状态。通常情况下,IDLE_TIMEOUT 的默认值是 300秒。
此外,已经接收到 GOAWAY 的 channel 在没有活跃(active)或挂起(pending)的 RPCs 时,也应当转换到 IDLE 状态,以避免尝试断开连接的服务器上的连接过载。
SHUTDOWN:
标识该 channel 已经开始关闭。任何新的 RPCs 都应该立即失败。待处理(pending)的 RPCs 可能会继续运行,直到应用程序取消它们。channel 可能会因为应用程序显式请求关闭,或者在尝试连接通信期间发生了不可恢复的错误而进入此状态。(截至2015年12月6日,没有已知的错误(连接或通信时)被归类为不可恢复的错误。)
一旦 channel 进入 SHUTDOWN 状态,就绝不会再离开。也就是说,SHUTDOWN 是状态机的结束。
状态转换表
下表列出了合法的状态转换和相关原因。空的单元表示不允许对应的状态转换。
From/To | CONNECTING | READY | TRANSIENT_FAILURE | IDLE | SHUTDOWN |
---|---|---|---|---|---|
CONNECTING | Incremental progress during connection establishment | All steps needed to establish a connection succeeded | Any failure in any of the steps needed to establish connection | No RPC activity on channel for IDLE_TIMEOUT | Shutdown triggered by application. |
READY | Incremental successful communication on established channel. | Any failure encountered while expecting successful communication on established channel. | No RPC activity on channel for IDLE_TIMEOUT OR upon receiving a GOAWAY while there are no pending RPCs. | Shutdown triggered by application. | |
TRANSIENT_FAILURE | Wait time required to implement (exponential) backoff is over. | Shutdown triggered by application. | |||
IDLE | Any new RPC activity on the channel | Shutdown triggered by application. | |||
SHUTDOWN |
Channel State API
所有的 gRPC 库都应该暴露一个 channel-level 的 API 方法以轮询 channel 的当前状态。
在 C++ 中, 这个方法叫 GetState,它会返回一个标识五个合法状态中一个的枚举值。如果 channel 当前处于 IDLE 状态,它还接受布尔值 try_to_connect 以转换为 CONNECTING。The boolean should act as if an RPC occurred, so it should also reset IDLE_TIMEOUT.
grpc_connectivity_state GetState(bool try_to_connect);
所有的 gRPC 库还应该暴露一个 API 以在当 channel 状态发生变化时,应用程序(使用 gRPC API 的用户)可以得到通知。由于状态更改可以快速并且与任何此类通知竞争,因此通知应该仅通知用户已经发生了一些状态改变,将其留给用户以轮询该通道以获得当前状态。异步版本的这个 API如下:
bool WaitForStateChange(grpc_connectivity_state source_state, gpr_timespec deadline);
当状态不是 source_state 时返回 true,如果截止时间到期则返回 false。Asynchronous- and futures-based 的API应该有一个相应的方法,允许在通道状态发生变化时通知应用程序。
请注意,每当从任何状态转换到任何其他状态时,都会发送通知。
也就是说,合法状态转换的规则要求从 CONNECTING 转换到 TRANSIENT_FAILURE 并返回到每个可恢复故障的 CONNECTING,即使相应的指数退避在重试之前不需要等待。综合效果是应用程序可能会收到看似虚假的状态更改通知。例如,在 CONNECTING 状态的 channel 上等待状态变更的应用程序,可能会在收到状态更改通知,但在轮询当前状态时发现其依然处于 CONNECTING 状态,因为该 channel 可能在 TRANSIENT_FAILURE 状态下花费了极少的时间。