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 网络科学导论,网络控制
  • ¥100 安卓tv程序连接SQLSERVER2008问题
  • ¥15 利用Sentinel-2和Landsat8做一个水库的长时序NDVI的对比,为什么Snetinel-2计算的结果最小值特别小,而Lansat8就很平均
  • ¥15 metadata提取的PDF元数据,如何转换为一个Excel
  • ¥15 关于arduino编程toCharArray()函数的使用
  • ¥100 vc++混合CEF采用CLR方式编译报错
  • ¥15 coze 的插件输入飞书多维表格 app_token 后一直显示错误,如何解决?
  • ¥15 vite+vue3+plyr播放本地public文件夹下视频无法加载
  • ¥15 c#逐行读取txt文本,但是每一行里面数据之间空格数量不同
  • ¥50 如何openEuler 22.03上安装配置drbd