我有一个用 C++ 编写的 gRPC 服务器和一个用 Java 编写的客户端。 使用阻塞存根一切工作正常。然后我决定将其中一个调用更改为异步,因此我在客户端中创建了一个额外的存根,该存根是使用 newStub(channel) 而不是 newBlockingStub(channel) 创建的。我没有在服务器端进行任何更改。这是一个简单的一元 RPC 调用。
所以我改变了
Empty response = blockingStub.callMethod(request);
到
asyncStub.callMethod(request, new StreamObserver<Empty>() {
@Override
public void onNext(Empty response) {
logInfo("asyncStub.callMethod.onNext");
}
@Override
public void onError(Throwable throwable) {
logError("asyncStub.callMethod.onError " + throwable.getMessage());
}
@Override
public void onCompleted() {
logInfo("asyncStub.callMethod.onCompleted");
}
});
从那时起,当我使用这个 RPC 时(大多数时候)就会调用 onError,它给出的错误是“CANCELLED:io.grpc.Context was cancelled without error”。我读到过有关在 RPC 调用中进行 RPC 调用时分叉 Context 对象的内容,但这里的情况并非如此。另外,上下文似乎是一个服务器端对象,我不明白它与客户端有何关系。这是传播回客户端的服务器端错误吗?在服务器端,一切似乎都成功完成,所以我不知道为什么会发生这种情况。在调用 asyncStub.callMethod 之后插入 1ms 睡眠似乎可以使这个问题消失,但达不到目的。任何和所有有助于理解这一点的帮助将不胜感激。
一些注意事项:
service EventHandler {
rpc callMethod(Msg) returns (Empty) {}
}
message Msg {
uint64 fieldA = 1;
int32 fieldB = 2;
string fieldC = 3;
string fieldD = 4;
}
message Empty {
}
事实证明我错了。客户端也使用上下文对象。解决方案是执行以下操作:
Context newContext = Context.current().fork();
Context origContext = newContext.attach();
try {
// TODO: Call async RPC here
} finally {
newContext.detach(origContext);
}
希望这可以帮助其他人。
我见过很多旧的答案提到使用
attach()/detach()
但 grpc 文档明确提到使用 run
代替。
这实际上解决了我的问题:
Context.current().fork().run {
doSend(request)
}
我和你遇到了同样的问题,你能把完整的代码贴在这里吗?谢谢。