最佳实践:"Soft Cancellation" 使用 gRPC
Best Practice: "Soft Cancellation" with gRPC
在我看来,gRPC 的内置取消支持在以下意义上非常激进:如果我在客户端调用取消,通信通道将立即关闭。服务器得到通知并可以进行清理工作,但是清理完成后似乎没有机会通知客户端。
如果我想要以下“软取消”行为,我应该怎么做?
- 客户请求取消
- 服务器收到取消请求并开始清理
- 服务器完成清理并关闭通信通道
- 客户收到取消程序已完成的通知
可以使用请求流和 oneof
关键字来实现此行为:
service ServiceName{
rpc Communicate (stream Request) returns (Response);
}
message Request {
oneof request_oneof {
ActualRequest actual = 1;
CancellationRequest cancellation = 2;
}
}
这实现起来应该不难,但看起来也很麻烦。您认为预期的方式是什么?
对于一元 RPC,除了取消之外别无选择。您需要将其设为 client-streaming RPC 以便对其他通信事件进行编码。
对于 client-streaming 和 bidi-streaming RPC,一个常见的模式是客户端延迟 half-close。当客户端完成时,客户端 half-closes 通知服务器客户端已完成。服务器然后执行清理并可以使用状态消息干净地关闭流。您可以采用相同的方法,利用 half-close:
service ServiceName {
rpc Communicate (stream ActualRequest) returns (Response);
}
message ActualRequest {...}
Streaming 允许您创建这样的自定义协议,但只有您的应用程序知道如何与该协议交互。例如,代理无法启动此协议的“优雅取消”。所以你应该只在你真正需要它的时候使用它,你仍然应该正确地实现“硬”取消,即使你可能不经常使用它。
在我看来,gRPC 的内置取消支持在以下意义上非常激进:如果我在客户端调用取消,通信通道将立即关闭。服务器得到通知并可以进行清理工作,但是清理完成后似乎没有机会通知客户端。
如果我想要以下“软取消”行为,我应该怎么做?
- 客户请求取消
- 服务器收到取消请求并开始清理
- 服务器完成清理并关闭通信通道
- 客户收到取消程序已完成的通知
可以使用请求流和 oneof
关键字来实现此行为:
service ServiceName{
rpc Communicate (stream Request) returns (Response);
}
message Request {
oneof request_oneof {
ActualRequest actual = 1;
CancellationRequest cancellation = 2;
}
}
这实现起来应该不难,但看起来也很麻烦。您认为预期的方式是什么?
对于一元 RPC,除了取消之外别无选择。您需要将其设为 client-streaming RPC 以便对其他通信事件进行编码。
对于 client-streaming 和 bidi-streaming RPC,一个常见的模式是客户端延迟 half-close。当客户端完成时,客户端 half-closes 通知服务器客户端已完成。服务器然后执行清理并可以使用状态消息干净地关闭流。您可以采用相同的方法,利用 half-close:
service ServiceName {
rpc Communicate (stream ActualRequest) returns (Response);
}
message ActualRequest {...}
Streaming 允许您创建这样的自定义协议,但只有您的应用程序知道如何与该协议交互。例如,代理无法启动此协议的“优雅取消”。所以你应该只在你真正需要它的时候使用它,你仍然应该正确地实现“硬”取消,即使你可能不经常使用它。