[英]Detect client context destruction from gRPC server
我创建了一个异步 C++ gRPC 服务器,它提供了几个类似的 API,其签名类似于:
service Foo {
rpc FunctionalityA(ARequest) returns (stream AResponse);
rpc FunctionalityB(BRequest) returns (stream BResponse);
}
客户端创建一个通道来连接该服务,并使用来自不同线程的各种 RPC 调用,如下所示:
class FooClient {
// ...
void FunctionalityA() {
auto stub = example::Foo::NewStub(m_channel);
grpc::ClientContext context;
example::ARequest request;
example::AResponse response;
auto reader = stub->FunctionalityA(&context, request);
for(int i = 0; i < 3; i++) {
reader->Read(&response);
}
}
void FunctionalityB() {
auto stub = example::Foo::NewStub(m_channel);
grpc::ClientContext context;
example::BRequest request;
example::BResponse response;
auto reader = stub->FunctionalityB(&context, request);
for(int i = 0; i < 3; i++) {
reader->Read(&response);
}
}
// ...
};
int main() {
// ...
FooClient client(grpc::CreateChannel("127.0.0.1:12345", grpc::InsecureChannelCredentials()));
auto ta = std::thread(&FooClient::FunctionalityA, &client);
auto tb = std::thread(&FooClient::FunctionalityB, &client);
// ...
}
我想实现服务器,以便:
我面临的问题是,即使与两个功能之一关联的 ClientContext 超出 scope (在示例中的 3 次读取之后),服务器也没有收到任何信息并继续写入,并且“ok”状态保持为真。 “ok”状态变为 false 并允许我仅在客户端断开连接时停止写入。
这是 gRPC 的预期行为吗? 客户端是否需要发送特定的“死亡之吻”消息以通知服务器停止在 stream 上写入?
为了完整起见,这里是一个功能服务器端的实现示例:
void FunctionalityB::ProcessRequest(bool ok, RequestState state) {
if(!ok) {
if(state == RequestState::START) {
// the server has been Shutdown before this particular call got matched to an incoming RPC
delete this;
} else if(state == RequestState::WRITE || state == RequestState::FINISH) {
// not going to the wire because the call is already dead (i.e., canceled, deadline expired, other side dropped the channel, etc).
delete this;
} else {
// unhandled state
}
} else {
if(state == RequestState::START) {
// the RPC has indeed been started
m_writer.Write(m_response, CreateTag(RequestState::WRITE));
// the constructor of the functionality requests a new one to handle future new connections
new FunctionalityB(m_completion_queue, m_service, m_worker);
} else if(state == RequestState::WRITE) {
// TODO do some real work
std::this_thread::sleep_for(std::chrono::milliseconds(50));
m_writer.Write(m_response, CreateTag(RequestState::WRITE)); // this write will continue forever, even after client stops reading and TryCancel its context
} else if(state == RequestState::FINISH) {
delete this;
} else {
// unhandled state
}
}
}
有两种方法可以检测服务器上的呼叫取消。
第一个是检查ServerContext::IsCancelled()
。 这是你可以在写之前检查的东西,在这种情况下可能没问题。 但是,在一般情况下,它可能并不理想,因为您的应用程序可能会在执行另一次写入之前等待一些其他事件(除了先前的写入完成),并且理想情况下,您希望在取消时以某种异步方式获得通知发生。
这让我想到了第二种方法,即在 RPC 开始之前通过调用ServerContext::AsyncNotifyWhenDone()
来取消调用时在完成队列上请求一个事件。 这将为您提供取消的异步通知,但不幸的是,API 非常笨重并且有一些锋利的边缘。 (这在新的基于回调的 API中处理得更加干净,但是在我们完成EventEngine工作之前,API 在 OSS 中的性能并不那么好。)
我希望这个信息是有帮助的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.