douben7260 2019-05-13 20:58
浏览 259
已采纳

gRPC是否可以提供零服务器消息?

In the following gRPC-client code, is the second if necessary?

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") 
}

Intuitively, one should always check for nil in go just in case. However, there is a runtime check to catch any client-to-server nil usage e.g.

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
}

So is there a similar server-to-client runtime guarantee, and thus remove the need for a status == nil check?

  • 写回答

2条回答 默认 最新

  • dougutuo9879 2019-05-14 20:23
    关注

    Investigating further with a contrived server example:

    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)
    }
    

    and testing client/server reactions:

    CLIENT:

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

    SERVER:

    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
    

    So even if one wanted to legitimately return a nil value, the gRPC transport will not allow it.

    Note: the server-side code is still executed - as expected - but as far as the client is concerned, the gRPC call failed.

    Conclusion: a valid (err==nil) server response will always return a valid (non-nil) message.


    EDIT:

    Inspecting the gRPC source reveals where a nil message is caught:

    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())
        }
        // ...
    }
    

    The comment in this line is key:

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

    So if one were to use reflect on our typed-nil, reflect.ValueOf(msg).IsNil() would return true. The following c.Marshal(msg) errors - and the call fails to send a message response to the client.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 雷达RD数据集,生成的RD图完全不理解
  • ¥15 Cesm如何关闭不用的模块呢
  • ¥15 vue2两层数据导出为excle
  • ¥15 有人能帮我做一下这个项目吗
  • ¥15 网络上传速率比下载速率高
  • ¥15 (标签-游戏|关键词-网页游戏)
  • ¥15 这个咋搞🥹可能需要手把手教
  • ¥15 RFID标签芯片ROCKY100配置成SPI主机问题
  • ¥15 接口和关键字的问题,多选
  • ¥15 yolov5s模型下载就卡住,没有运行结果