首页 > 解决方案 > 添加新消息后的 Proto2 编码/解码问题

问题描述

我对协议缓冲区相当陌生,但一直在尝试将它们作为通过 MQTT 发送数据的一种方式来学习。到目前为止,我已经很好地创建了 proto 消息并为 python 运行时编译它们,直到我开始注意到我的 protobufs 版本之间的不兼容。

当我向服务器端原型定义添加消息类型(不更改现有消息/字段)而不更新客户端原型定义时,解码发送到服务器的消息会给我不确定的结果。

这是我正在谈论的一个例子:

客户端原型:

message Wrapper {
    optional uint32 id = 1;
    optional string name = 2;
    oneof payload {
        Event event = 3;
        Command command = 4;
    }
}

message Event {
    uint32 event_id = 1;
    oneof event_payload {
        LoginEvent login_event = 2;
        LogoffEvent logoff_event = 3;
    }
}

服务器原型:

message Wrapper {
    optional uint32 id = 1;
    optional string name = 2;
    oneof payload {
        Event event = 3;
        Command command = 4;
    }

message Event {
    uint32 event_id = 1;
    oneof event_payload {
        LoginEvent login_event = 2;
        LogoffEvent logoff_event = 3;
        NewUserEvent new_user_event = 4;
    }
}

我将编码并从客户端发送一条消息:

message Wrapper {
    id = 12345;
    name = John;
    event = {
        login_event = ...
    }
}

并将在服务器上解码消息并获取:

message Wrapper {
    id = 12345;
    name = John;
    event = {
        logoff_event = ...
    }
}

注意:解码的消息类型不是确定性的,消息之间会发生变化

有人可以解释为什么添加事件类型似乎会破坏解码吗?或者我应该遵循什么最佳实践来提高版本兼容性?提前致谢!

标签: protocol-buffersversiongrpcdecodeencode

解决方案


可能是 Python 实现的副作用,而不是错误。

您不包含用于(取消)编组 protobuf 的代码。

在您的示例中,是否logoff_event包含LogoffEvent消息类型?

你说不确定性?event_payload对于客户端发送的同一消息,您是否在服务器上看到不同的消息类型?

返回的内容:

msg = Wrapper() # Your decoded message
assert msg.event.WhichOneof("event_payload")
# or
assert msg.event.HasField("login_event")

请参阅Python 生成的代码:OneOf

更新 210927

我无法重现您观察到的行为;它对我来说按预期工作。

客户:

import client_pb2

msg = client_pb2.Wrapper()

msg.id=0
msg.name="Test"
msg.event.event_id=1
msg.event.login_event.y="Hello Freddie"

print(msg)

f=open("message","wb")
f.write(msg.SerializeToString())
f.close()

产量:

id: 0
name: "Test"
event {
  event_id: 1
  login_event {
    y: "Hello Freddie"
  }
}

服务器:

import server_pb2

msg = server_pb2.Wrapper()

f=open("message","rb")
msg.ParseFromString(f.read())
f.close

assert msg.event.WhichOneof("event_payload")
assert msg.event.HasField("login_event")

print(msg)

产量:

id: 0
name: "Test"
event {
  event_id: 1
  login_event {
    y: "Hello Freddie"
  }
}

推荐阅读