使用以下proto缓冲区代码:
syntax = "proto3";
package pb;
message SimpleRequest {
int64 number = 1;
}
message SimpleResponse {
int64 doubled = 1;
}
// All the calls in this serivce preform the action of doubling a number.
// The streams will continuously send the next double, eg. 1, 2, 4, 8, 16.
service Test {
// This RPC streams from the server only.
rpc Downstream(SimpleRequest) returns (stream SimpleResponse);
}
我能够成功打开一个流,并不断从服务器获取下一个加号。
我的运行代码如下:
ctxDownstream, cancel := context.WithCancel(ctx)
downstream, err := testClient.Downstream(ctxDownstream, &pb.SimpleRequest{Number: 1})
for {
responseDownstream, err := downstream.Recv()
if err != io.EOF {
println(fmt.Sprintf("downstream response: %d, error: %v", responseDownstream.Doubled, err))
if responseDownstream.Doubled >= 32 {
break
}
}
}
cancel() // !!This is not a graceful shutdown
println(fmt.Sprintf("%v", downstream.Trailer()))
我遇到的问题是使用上下文取消意味着我的downstream.Trailer()响应为空。有没有办法从客户端优雅地关闭此连接并接收downstream.Trailer()。
注意:如果我从服务器端关闭下游连接,则会填充我的预告片。但我无法指示我的服务器端关闭此特定流。因此必须有一种方法可以优雅地关闭流客户端。
感谢。
根据要求提供了一些服务器代码:
func (b *binding) Downstream(req *pb.SimpleRequest, stream pb.Test_DownstreamServer) error {
request := req
r := make(chan *pb.SimpleResponse)
e := make(chan error)
ticker := time.NewTicker(200 * time.Millisecond)
defer func() { ticker.Stop(); close(r); close(e) }()
go func() {
defer func() { recover() }()
for {
select {
case <-ticker.C:
response, err := b.Endpoint(stream.Context(), request)
if err != nil {
e <- err
}
r <- response
}
}
}()
for {
select {
case err := <-e:
return err
case response := <-r:
if err := stream.Send(response); err != nil {
return err
}
request.Number = response.Doubled
case <-stream.Context().Done():
return nil
}
}
}
您仍需要使用一些信息填充预告片。我使用grpc.StreamServerInterceptor来做到这一点。
答案 0 :(得分:0)
根据grpc go文档
预告片从服务器返回预告片元数据(如果有)。 只能在stream.CloseAndRecv返回之后调用它,或者 stream.Recv返回了一个非零错误(包括io.EOF)。
因此,如果您想在客户端阅读预告片,请尝试使用此类
ctxDownstream, cancel := context.WithCancel(ctx)
defer cancel()
for {
...
// on error or EOF
break;
}
println(fmt.Sprintf("%v", downstream.Trailer()))
出现错误时打开infinate循环并打印预告片。 <{1}}将在函数结束时调用,因为它是延迟的。
答案 1 :(得分:0)
我无法找到明确解释它的参考文献,但这似乎不可能。
在线路上,当呼叫正常完成时(即服务器退出呼叫),grpc-status
后面跟着预告片元数据。
当客户取消呼叫时,这些都不会被发送。
似乎gRPC将呼叫取消视为rpc的快速中止,与被丢弃的套接字没什么不同。
添加&#34;取消消息&#34;通过请求流媒体工作;服务器可以选择它并从其末尾取消流,并且仍会发送预告片:
message SimpleRequest {
oneof RequestType {
int64 number = 1;
bool cancel = 2;
}
}
....
rpc Downstream(stream SimpleRequest) returns (stream SimpleResponse);
虽然这确实给代码增加了一些复杂性。