【问题标题】:is a nil server message possible with gRPC?gRPC 是否可以发送零服务器消息?
【发布时间】:2019-09-30 20:28:31
【问题描述】:

在下面的gRPC-client 代码中,是否需要第二个if

status, err := cli.GetStatus(ctx, &empty.Empty{})
if err != nil {
    return err
}

if status == nil {
    // this should NEVER happen - right?
    return fmt.Errorf("nil Status result returned") 
}

直观地说,应该始终检查 go 中的 nil 以防万一。 但是,有一个运行时检查来捕获任何客户端到服务器的 nil 用法,例如

status, err := cli.GetStatus(ctx, nil) // <- runtime error

if err != nil {
    // "rpc error: code = Internal desc = grpc: error while marshaling: proto: Marshal called with nil"
    return err
}

那么是否有类似的服务器到客户端运行时保证,从而消除了对status == nil 检查的需要?

【问题讨论】:

    标签: go grpc grpc-go


    【解决方案1】:

    使用人为的服务器示例进一步调查:

    func (s *mygRPC) GetStatus(context.Context, *empty.Empty) (*pb.Status, error) {
        log.Println("cli: GetStatus()")
    
        //return &pb.Status{}, nil
        return nil, nil // <- can server return a nil status message (with nil error)
    }
    

    并测试客户端/服务器的反应:

    客户:

    ERROR: rpc error: code = Internal desc = grpc: error while marshaling: proto: Marshal called with nil
    

    服务器:

    2019/05/14 16:09:50 cli: GetStatus()
    ERROR: 2019/05/14 16:09:50 grpc: server failed to encode response:  rpc error: code = Internal desc = grpc: error while marshaling: proto: Marshal called with nil
    

    因此,即使想要合法地返回一个 nil 值,gRPC 传输也不会允许它。

    注意:服务器端代码仍按预期执行,但就客户端而言,gRPC 调用失败。

    结论:一个有效的(err==nil)服务器响应将总是返回一个有效的(非nil)消息。


    编辑:

    检查gRPC 源可以发现nil 消息被捕获的位置:

    server.go

    func (s *Server) sendResponse(t transport.ServerTransport, stream *transport.Stream, msg interface{}, cp Compressor, opts *transport.Options, comp encoding.Compressor) error {
        data, err := encode(s.getCodec(stream.ContentSubtype()), msg)
        if err != nil {
            grpclog.Errorln("grpc: server failed to encode response: ", err)
            return err
        }
        // ...
    }
    

    rpc_util.go

    func encode(c baseCodec, msg interface{}) ([]byte, error) {
        if msg == nil { // NOTE: typed nils will not be caught by this check
            return nil, nil
        }
        b, err := c.Marshal(msg)
        if err != nil {
            return nil, status.Errorf(codes.Internal, "grpc: error while marshaling: %v", err.Error())
        }
        // ...
    }
    

    这一行的注释很关键:

    if msg == nil { // NOTE: typed nils will not be caught by this check }
    

    因此,如果对我们的 typed-nil 使用反射,reflect.ValueOf(msg).IsNil() 将返回 true。以下c.Marshal(msg) 错误 - 调用未能向客户端发送消息响应。

    【讨论】:

      【解决方案2】:

      是的,这绝不应该发生。 GRPC 对此负责。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-11-03
        • 2019-01-06
        • 1970-01-01
        • 2017-04-08
        • 1970-01-01
        • 2021-04-16
        • 2019-11-12
        • 1970-01-01
        相关资源
        最近更新 更多