问题描述
我正在编写一个围绕 gRPC 一元调用的包装器,但我遇到了一个问题:假设我有一个 ClientAsyncResponseReader
对象,它被创建并开始一个像这样的请求
response_reader_ = std::unique_ptr<grpc::ClientAsyncResponseReader<ResponseType>>(
grpc::internal::ClientAsyncResponseReaderFactory<ResponseType>::Create(
channel.get(),completion_queue,rpc_method,&client_context_,request,true
)
);
response_reader_->Finish(
response_sharedptr_.get(),status_sharedptr_.get(),static_cast<void*>(some_tag)
);
// Set a breakpoint here
其中所有参数都有效。
我的印象是,当 Finish
调用返回时,request
对象肯定已通过网络发送出去。但是,通过在 Finish() 调用之后设置断点(在客户端程序中,要清楚)并检查我的服务器的日志,我发现服务器直到我恢复后才记录 request
来自断点。
这似乎表明我需要等待其他事情才能确保请求真正发送出去:而且,执行上述代码的线程在发送请求方面仍然具有某种作用出现在断点后。
当然,也许我的假设是错误的,并且服务器不会在请求进来时立即记录请求。如果不是,那么显然我不太了解 gRPC 的语义,所以我希望获得一些更有经验的见解。
您可以查看我的一元调用抽象 here 的代码。它应该足够了,但如果需要其他任何东西,我很乐意提供。
编辑:情节变厚了。在服务器处理程序上为传入请求设置断点后,看起来对 Finish 的调用通常确实“确保”请求已发出:except 对于进程发送的第一个请求。我猜在 grpc::channel
或什至可能在 grpc::completion_queue
中维护了一些状态,这会延迟初始请求
解决方法
来自文档
response_reader_ = std::unique_ptr<grpc::ClientAsyncResponseReader<ResponseType>>(
grpc::internal::ClientAsyncResponseReaderFactory<ResponseType>::Create(
channel.get(),completion_queue,rpc_method,&client_context_,request,true
)
);
这将开始一个呼叫并写出请求(start=true)。该函数没有标签参数。因此,完成队列无法通知呼叫开始何时完成。调用 RPC 方法有点复杂,它基本上涉及创建网络数据包并将其放入线路。如果传输出现瞬时故障或通道完全消失或用户做了一些愚蠢的事情,它可能会失败。另一件事,为什么我们需要标记通知是完成队列确实是一个争用点。所有 RPC 对象都与此对话,可能会发生完成队列未空闲且请求仍处于待处理状态。
response_reader_->Finish(
response_sharedptr_.get(),status_sharedptr_.get(),static_cast<void*>(some_tag)
这将请求 RPC 运行时接收服务器的响应。输出是当服务器响应到达时,完成队列将通知客户端。在此刻。我们假设客户端没有错误,一切正常并且请求已经在进行中。因此对于一元 rpc,Finish call 的状态永远不会为 false。
这似乎表明我需要等待其他事情才能确保请求真正发送出去:而且,执行上述代码的线程在发送请求方面仍然具有某种作用出现在断点后。
也许,您想重用请求对象(我对此做了一些实验)。对我来说,我将请求对象保存在内存中,直到响应到达。无法保证在创建调用后不会需要请求对象。