英文:
protobuf messages failing to deserialise due to message size
问题
消息无法从字节反序列化为任何类型的Any,错误消息为:proto: 无法解析无效的线格式数据,但只有当所有消息字段都有值时才会出现此错误。就好像消息有一个大小限制,空或部分为空的消息可以正常反序列化,但如果所有字段都有值,就会失败。这些消息来自于一个从Dapr适配器到Redis流的适配器。
import (
"github.com/golang/protobuf/proto"
"github.com/golang/protobuf/ptypes/any"
)
func (h *EventHandler) instanceHandler(ctx context.Context, e *common.TopicEvent) (bool, error) {
var anyMsg any.Any
b := []byte(e.Data.(string))
err := proto.Unmarshal(b, &anyMsg)
if err != nil {
h.logger.Error().
Str("eventId", e.ID).
Str("topic", e.Topic).
Str("source", e.Source).
Err(err).
Msg("")
return false, err
}
如果所有消息字段都有值,上述代码将失败,消息示例:
message GetElementsPaginatedRequest {
string eid = 1;
string mediatorID = 2;
string callerID = 3;
string accountID = 4;
string streamID = 5;
int32 limit = 6;
int32 page = 7;
string order = 8;
string clientID = 9;
}
在发送到网络之前进行序列化:
err := anyMsg.MarshalFrom(&epb)
if err != nil {
return err
}
b, err := proto.Marshal(&anyMsg)
if err != nil {
return err
}
如果我删除一个字段(任何字段),例如GetElementsPaginatedRequest.accountID,反序列化将成功。
在本地测试中,序列化和反序列化也正常工作,这种行为似乎只发生在从Dapr适配器接收的消息中。
无法复制此问题,对于不是通过Dapr和Redis接收的消息。在本地测试中,无论消息的大小如何,反序列化都正常工作。
英文:
Messages are failing to deserialise from bytes to any.Any with the error message: proto: cannot parse invalid wire-format data but only when all the message fields have values, it's as if there's a size limit on the messages, empty or partially empty messages deserialise fine, but if all fields have values it will fail. The messages are coming from a Dapr adapter to a redis stream.
import (
"github.com/golang/protobuf/proto"
"github.com/golang/protobuf/ptypes/any"
)
func (h *EventHandler) instanceHandler(ctx context.Context, e *common.TopicEvent) (bool, error) {
var anyMsg any.Any
b := []byte(e.Data.(string))
err := proto.Unmarshal(b, &anyMsg)
if err != nil {
h.logger.Error().
Str("eventId", e.ID).
Str("topic", e.Topic).
Str("source", e.Source).
Err(err).
Msg("")
return false, err
}
The above will fail if all the message fields have values, message example:
message GetElementsPaginatedRequest {
string eid = 1;
string mediatorID = 2;
string callerID = 3;
string accountID = 4;
string streamID = 5;
int32 limit = 6;
int32 page = 7;
string order = 8;
string clientID = 9;
}
Serialise before sending over the wire:
err := anyMsg.MarshalFrom(&epb)
if err != nil {
return err
}
b, err := proto.Marshal(&anyMsg)
if err != nil {
return err
}
If I remove one field (any field) for example, GetElementsPaginatedRequest.accountID, deserialisation will succeed.
Testing serialisation and deserialisation also works fine within a local test, this behaviour just seems to occur for messages received from the Dapr adapter.
Unable to replicate this issue for messages that are not received via Dapr and redis. In a local test deserialisation works fine, regardless of the size of the message.
答案1
得分: 1
这个字节大小差异的行为可以通过在发布者客户端上设置 content-type,并将接收器更改为从 TopicEvent.Data 解组为 TopicEvent.RawData(在设置了 context-type 后可能)来避免。
新的发布者配置
opts := []dapr.PublishEventOption{
dapr.PublishEventWithContentType("application/octet-stream"),
}
err = client.PublishEvent(ctx, pubsubName, topic, b, opts...)
然后从 RawData 字段进行 proto.Unmarshal。
err := proto.Unmarshal(e.RawData, &anyMsg)
字节匹配,接收器可以解码。
英文:
This bytes size difference behaviour can be avoided by setting the content-type on the publisher client and changing the receiver to unmarshal from TopicEvent.Data to TopicEvent.RawData (which is possible after setting the context-type).
New publisher configuration
opts := []dapr.PublishEventOption{
dapr.PublishEventWithContentType("application/octet-stream"),
}
err = client.PublishEvent(ctx, pubsubName, topic, b, opts...)
And proto.Unmarshal from the RawData field.
err := proto.Unmarshal(e.RawData, &anyMsg)
The bytes match and the receiver can decode.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论