gRPC 客户端失败 "CANCELLED: io.grpc.Context was cancelled without error"

gRPC client failing with "CANCELLED: io.grpc.Context was cancelled without error"

我有一个用 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 {

}

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

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

希望这对以后的其他人有所帮助。