我正在尝试通过gRPC创建流API,但是由于某些奇怪的原因,我的StreamObserver被取消了。
这是我的.proto
声明:
service Service {
rpc connect (ConnectionRequest) returns (stream StreamResponse) {}
rpc act (stream ActRequest) returns (ActResponse) {}
}
这个想法是,用户将connect
留给每个用户StreamResponse
,然后在每个act
上StreamResponse
将收到更新。这是Service
class ServiceImpl extends ServiceGrpc.ServiceImplBase {
............
@Override
public void connect(ConnectionRequest request, StreamObserver<StreamResponse> responseObserver) {
observers.add(responseObserver);
}
@Override
public StreamObserver<ActRequest> act(StreamObserver<ActResponse> responseObserver) {
return return new StreamObserver<ActRequest>() {
@Override
public void onNext(ActRequest actRequest) {
StreamResponse streamResponse = StreamResponse.newBuilder().build();
observers.forEach(o -> o.onNext(streamResponse));
actRequest..onCompleted();
}
...............
};
}
..................
}
这是客户端connect
方法:
ServiceGrpc.ServiceStub stub = ServiceGrpc.newStub(channel);
new StreamObserver<PlayerResponse>() {
........
@Override
public void onNext(StreamResponse streamResponse) {
logger.info("Act: " streamResponse.getData());
}
........
}
ConnectionRequest request = ConnectionRequest.newBuilder().build();
stub.connect(request, streamObserver);
还有客户端act
方法:
StreamObserver<ActResponse> observer = new StreamObserver<ActResponse>() {
@Override
public void onNext(ActResponse actResponse) {
logger.info("Status: " + actResponse.getSuccess());
}
.........
};
StreamObserver<ActRequest> act = stub.act(observer);
act.onNext(MoveRequest.newBuilder().build());
act.onCompleted();
当我同时启动客户端和服务器时,客户端可以呼叫connect
。但是方法act
只能被第一次调用。当客户第二次致电act
时,我收到以下异常:
io.grpc.StatusRuntimeException: CANCELLED: call already cancelled
at io.grpc.Status.asRuntimeException(Status.java:517)
at io.grpc.stub.ServerCalls$ServerCallStreamObserverImpl.onNext(ServerCalls.java:335)
在调试模式下,我可以看到StreamObserver<StreamResponse> responseObserver
是canceled=true
。
答案 0 :(得分:1)
我发现了为什么会出现此错误。在客户端中,我使用Vaadin
来构建UI。所以StreamObserver<ActResponse>#onNext
我正在调用一个Vaddin方法,该方法引发异常。
我在开发过程中犯的错误是我将onError
保留为PoC空着。但是这个错误使我花费了几个小时的调试时间。
这是愚蠢的错误,但是我可以在这里保留,也许可以节省一些时间。