添加新消息后的 Proto2 编码/解码问题

Posted

技术标签:

【中文标题】添加新消息后的 Proto2 编码/解码问题【英文标题】:Proto2 encode/decode issues after adding new message 【发布时间】:2021-11-17 23:44:19 【问题描述】:

我对协议缓冲区还很陌生,但一直在尝试将它们作为通过 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 = ...
    

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

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

【问题讨论】:

【参考方案1】:

可能是 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 Generated Code: 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"
  

【讨论】:

是的,logff_event 确实包含 LogoffEvent 消息类型,是的,这正是我所说的非确定性。不过,我可能对此不正确,我可能只是缺少一个映射/模式。此外,为了解组,我使用 FromString 方法,然后使用 json_format.MessageToJson 将 msg 转换为 json,我最终将其加载为 python 字典。 我已经更新了我的答案。它按我的预期工作。

以上是关于添加新消息后的 Proto2 编码/解码问题的主要内容,如果未能解决你的问题,请参考以下文章

基于Netty实现Redis协议的编码解码器

如何在 shell 中解码 URL 编码的字符串?

定义了protobuf-net序列化所需的proto2消息类型?

如何使用 Avro 二进制编码器对 Kafka 消息进行编码/解码?

高级编码和解码技术

PHP IMAP 解码消息