Protobuf Any类型UnmarshalNew报错‘proto: not found’的解决方法
问题描述
我有一个使用Any类型的Protobuf API,定义如下:
message Document { DocumentMeta meta = 1; bytes data = 2; google.protobuf.Any details = 3; }
客户端创建Dog消息并序列化为details字段:
dog1 := events2.Dog{ Name: "Leo", Id: 1, } var dst anypb.Any err = anypb.MarshalFrom(&dst, &dog1, proto.MarshalOptions{})
服务端已复制包含Dog定义的.pb文件并生成对应的Go文件,打印Document时能看到正确信息:
Any:type_url:"type.googleapis.com/com.test.eventbus.pb.events.Dog" value:"\n\x03Leo\x10\x01"
但服务端使用anypb.UnmarshalNew反序列化时未指定具体类型(需支持客户端动态传入启动时未定义的消息类型),代码如下:
basicDog, err := anypb.UnmarshalNew(doc.GetDetails(), proto.UnmarshalOptions{})
此时报错proto: not found。客户端会生成新的proto文件及对应的Go文件并复制到服务端,请问如何在服务端使用这些文件完成Any类型的反序列化?
解决方案
要实现动态反序列化google.protobuf.Any类型,核心是让Protobuf的类型注册表能识别客户端传入的新消息类型,具体步骤如下:
1. 确保生成的Go代码自动注册类型
用protoc-gen-go生成Go代码时,默认会在init()函数中将消息类型注册到全局类型注册表。检查生成的dog.pb.go文件,确认存在类似以下的注册逻辑:
func init() { proto.RegisterType((*Dog)(nil), "com.test.eventbus.pb.events.Dog") }
如果没有,重新执行生成命令确保参数正确:
protoc --go_out=. --go_opt=paths=source_relative your_proto_file.proto
2. 处理运行时新增的消息类型
如果客户端是在服务端启动后才推送新的Go文件,有两种处理方式:
- 编译为Go插件动态加载:将新增的消息类型编译成
.so插件文件,服务端运行时加载插件,插件的init()函数会自动注册类型到全局注册表。
编译插件命令:
服务端加载插件代码:go build -buildmode=plugin -o dog_plugin.so dog.pb.goplugin, err := plugin.Open("dog_plugin.so") if err != nil { // 处理加载错误 } // 插件加载完成后,类型已自动注册 - 重启服务加载新代码:若允许服务重启,将新Go文件放入服务端代码目录后,重新编译启动服务,新类型的
init()函数会执行并完成注册。
3. 完成反序列化
类型注册后,直接使用anypb.UnmarshalNew即可完成动态反序列化。如果需要更精细化的类型管理,也可以自定义类型注册表:
// 创建自定义注册表并添加目标类型 reg := proto.NewRegistry() reg.RegisterType((*events2.Dog)(nil), "com.test.eventbus.pb.events.Dog") // 使用自定义注册表反序列化 opts := proto.UnmarshalOptions{ Registry: reg, } basicDog, err := anypb.UnmarshalNew(doc.GetDetails(), opts)
核心原理
google.protobuf.Any的反序列化依赖Protobuf类型注册表,type_url中的类型名称必须与注册表中注册的完全匹配(包含完整包名)。只有类型被成功注册后,UnmarshalNew才能通过type_url找到对应的消息类型,完成反序列化。
内容的提问来源于stack exchange,提问作者uzumas
相关产品推荐
相关产品推荐

