gRPC 客户端失败,并显示“已取消:io.grpc.Context 已取消且没有错误”

问题描述 投票:0回答:3

我有一个用 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 睡眠似乎可以使这个问题消失,但达不到目的。任何和所有有助于理解这一点的帮助将不胜感激。

一些注意事项:

  1. 服务器端处理时间在1微秒左右
  2. 到目前为止,阻塞调用的往返时间是几百微秒(这是我试图缩短的时间,因为这本质上是一个 void 函数,所以我不需要等待响应)
  3. 此方法会连续调用多次,因此以前它会等待前一个方法完成,现在它们只是一个接一个地触发。
  4. 原型文件中的一些片段:
service EventHandler {
  rpc callMethod(Msg) returns (Empty) {}
}

message Msg {
  uint64 fieldA = 1;
  int32 fieldB = 2;
  string fieldC = 3;
  string fieldD = 4;
}

message Empty {

}
grpc grpc-java
3个回答
8
投票

事实证明我错了。客户端也使用上下文对象。解决方案是执行以下操作:

Context newContext = Context.current().fork();
Context origContext = newContext.attach();
try {
    // TODO: Call async RPC here
} finally {
    newContext.detach(origContext);
}

希望这可以帮助其他人。


0
投票

我见过很多旧的答案提到使用

attach()/detach()
但 grpc 文档明确提到使用
run
代替。

这实际上解决了我的问题:

Context.current().fork().run {
    doSend(request)
}

0
投票

我和你遇到了同样的问题,你能把完整的代码贴在这里吗?谢谢。

© www.soinside.com 2019 - 2024. All rights reserved.