Go客户端解析NATS传来的Protobuf报invalid wire-format data问题求助
解决Go客户端反序列化Protobuf数据报错
proto: cannot parse invalid wire-format data 错误原因分析
- 消息前缀未处理:服务器端发送的字节流包含额外前缀,并非纯Protobuf数据。
- 类型映射不匹配:C#端使用的
decimal、Guid类型与Go proto定义的string类型序列化格式不兼容。 - 潜在枚举不一致:C#与proto文件中的枚举数值可能存在错位。
具体修复步骤
1. 处理消息前缀
服务器端的序列化逻辑先写入1字节的FeedMessageType,再写入带Fixed32长度前缀的Protobuf数据。Go端需先跳过前缀,再提取有效Protobuf数据:
import "encoding/binary" _, err = sc.Subscribe("EXEC", func(m *stan.Msg) { if len(m.Data) < 5 { // 1字节类型标识 + 4字节Fixed32长度 fmt.Println("Invalid message length") return } // 跳过第一个字节的FeedMessageType dataAfterType := m.Data[1:] // 解析Fixed32格式的长度前缀(大端字节序) payloadLength := binary.BigEndian.Uint32(dataAfterType[:4]) // 提取实际的Protobuf payload protoPayload := dataAfterType[4 : 4+payloadLength] varr := &protos.Execution{} err = proto.Unmarshal(protoPayload, varr) if err != nil { fmt.Printf("Err unmarshalling!: %v\n\n", err.Error()) } else { fmt.Printf("Received a message: %+v\n", varr) } })
2. 修正类型映射
C#的decimal和Guid类型与官方Protobuf的string序列化格式不兼容,需调整proto定义:
修改.proto文件中的字段类型
// TradeData调整 message TradeData { int64 TradeId = 1; bytes Amount = 4; // 对应C# decimal(二进制格式) bytes ExecutionPrice = 5;// 对应C# decimal OrderStatus OrderStatus = 7; int64 AccountId = 11; bytes MatchedOrderExternalId = 14; // 对应C# Guid(16字节二进制) int64 MatchedOrderId = 16; bytes RemainingAmount = 17; // 对应C# decimal } // Execution调整 message Execution { string Origin = 4; OrderSide Side = 7; bytes RequestedPrice = 8; // 对应C# decimal bytes RequestedAmount = 9; // 对应C# decimal bytes RemainingAmount = 10; // 对应C# decimal int64 ExecutedAt = 13; OrderStatus OrderStatus = 14; repeated TradeData Trades = 16; OrderType OrderType = 20; int64 Version = 22; int64 AccountId = 23; RejectReason RejectReason = 25; ReportCause ReportCause = 26; bytes InstructionId = 27; // 对应C# Guid bytes ExternalOrderId = 28; // 对应C# Guid int32 ExecutionEngineMarketId = 29; int64 OrderId = 30; CancelReason CancelReason = 31; int64 TxId = 32; TimeInForce TimeInForce = 34; string CancelledBy = 35; }
Go端解析二进制数据
- 对于Guid(16字节):使用第三方库将bytes转为UUID:
import "github.com/google/uuid" // 解析MatchedOrderExternalId示例 uuidVal, err := uuid.FromBytes(varr.Trades[0].MatchedOrderExternalId) - 对于decimal:使用第三方库解析protobuf-net的decimal二进制格式(protobuf-net将decimal序列化为4个int32:flags, lo, mid, hi):
import ( "fmt" "math/big" "encoding/binary" "github.com/shopspring/decimal" ) func bytesToDecimal(b []byte) (decimal.Decimal, error) { if len(b) != 16 { return decimal.Zero, fmt.Errorf("invalid decimal bytes length") } flags := binary.LittleEndian.Uint32(b[:4]) lo := binary.LittleEndian.Uint32(b[4:8]) mid := binary.LittleEndian.Uint32(b[8:12]) hi := binary.LittleEndian.Uint32(b[12:16]) return decimal.FromBigInt(new(big.Int).SetUint64(uint64(hi)<<32|uint64(mid)), int32(flags>>16)&0xff), nil }
3. 验证枚举一致性
检查C#与proto文件中所有枚举的数值是否完全一致,例如:
- proto中
OrderStatus.Working = 0,C#中OrderStatus.Working的数值也必须为0 - 所有枚举项的数值一一对应,无遗漏或错位
4. 重新生成Go代码并测试
修改proto文件后,重新生成Go的Protobuf代码:
protoc --go_out=. execution.proto
然后运行Go客户端测试反序列化是否正常。
内容的提问来源于stack exchange,提问作者Divyalok Jaiswal
相关产品推荐
相关产品推荐

