在客户端应用程序和服务器端应用程序之间的 gRPC 连接中,客户端和服务器端都能够对调用是否成功在本地做出独立判断。例如,可以让同一个 RPC 在服务器端成功完成,但在客户端让其失败。类似地,在不同的情况下,客户端和服务器端可能会对同一个 RPC 得出不同的结论。但是,无论是客户端应用程序,还是服务器端应用程序,当希望终止 RPC 时,都可以通过取消该 RPC 来实现。一旦取消 RPC,就不能再进行与之相关的消息传递了,并且一方已经取消 RPC 的事实会传递到另一方。
在 Go 语言中,与截止时间类似,取消功能也是由 context 包实现的,其中 WithCancel 是一个内置函数。当 gRPC 应用程序调用该函数后,客户端的 gRPC 库会创建所需的 gRPC 头信息,表示客户端应用程序和服务器端应用程序之间的 gRPC 已终止。
以客户端应用程序和服务器端应用程序的双向流为例。在代码清单 5-6所示的 Go 代码示例中,可以通过 context.WithTimeout 获取cancel 函数。在得到 cancel 的引用之后,就可以在任何想终止 RPC的地方调用它。
代码清单 5-6 gRPC 取消
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) ➊
streamProcOrder, _ := client.ProcessOrders(ctx) ➋
_ = streamProcOrder.Send(&wrapper.StringValue{Value:"102"}) ➌
_ = streamProcOrder.Send(&wrapper.StringValue{Value:"103"})
_ = streamProcOrder.Send(&wrapper.StringValue{Value:"104"})
channel := make(chan bool, 1)
go asncClientBidirectionalRPC(streamProcOrder, channel)
time.Sleep(time.Millisecond * 1000)
// 取消RPC
cancel() ➍
log.Printf("RPC Status : %s", ctx.Err()) ➎
_ = streamProcOrder.Send(&wrapper.StringValue{Value:"101"})
_ = streamProcOrder.CloseSend()
<- channel
func asncClientBidirectionalRPC (streamProcOrder pb.OrderManagement_ProcessOrdersClient, c chan bool) {
...
combinedShipment, errProcOrder := streamProcOrder.Recv()
if errProcOrder != nil {
log.Printf("Error Receiving messages %v", errProcOrder) ➏
...
}
- ❶ 获取对 cancel 的引用。
- ❷ 调用流 RPC。
- ❸ 通过流发送消息给服务。
- ❹ 在客户端,取消 RPC(终止 RPC)。
- ❺ 当前上下文的状态。
- ❻ 当试图从已取消的上下文中接收消息时,会返回上下文已取消的错误。
当某一方取消 RPC 之后,另一方可以通过检查 context 来确定这一点。在本例中,服务器端应用程序可以通过使用stream.Context().Err() == context.Canceled,来检查当前的上下文是否已经取消。
正如以上例子所示,在 RPC 中处理错误是一个非常常见的需求。5.4 节将详细介绍 gRPC 的错误处理技术。
文档更新时间: 2023-09-02 05:45 作者:Minho