proto.MessageName返回空字符串

huangapple go评论72阅读模式
英文:

proto.MessageName returns empty string

问题

我在Go语言中遇到了一个关于Protobuf的问题。

给定以下代码片段:

if proto.MessageName(&messages.AddedItemEvent{}) == "" {
	log.Fatal("empty")
}

这将评估为true并退出应用程序。
什么情况下会导致protobuf在MessageName上返回空字符串?
我可以看到类型及其名称在生成的protobuf消息代码中已注册:

func init() {
	proto.RegisterType((*AddItemCommand)(nil), "messages.AddItemCommand")
	proto.RegisterType((*AddedItemEvent)(nil), "messages.AddedItemEvent")
	proto.RegisterType((*RenameCommand)(nil), "messages.RenameCommand")
	proto.RegisterType((*RenamedEvent)(nil), "messages.RenamedEvent")
	proto.RegisterType((*DumpCommand)(nil), "messages.DumpCommand")
}

我还验证了上述init实际运行了,确实如此。

我有其他生成的proto消息,它们按预期返回它们的名称。所以这里出了什么问题?

编辑
proto文件如下所示:

syntax = "proto3";
package messages;

//user messages
message AddItemCommand {
  string item = 1;
}
message AddedItemEvent {
  string item = 1;
}
message RenameCommand {
  string name = 1;
}
message RenamedEvent {
  string name = 1;
}
message DumpCommand {}

再次编辑。
main函数中手动调用
proto.RegisterType((*messages.AddedItemEvent)(nil), "messages.AddedItemEvent")

可以使其正常工作。
所以在messages.initmain之间的某个地方清除了类型注册表。

有人知道原因吗?

英文:

I'm completely stuck on a problem with Protobuf in Go

Given the following bit of code:

if proto.MessageName(&messages.AddedItemEvent{}) == "" {
	log.Fatal("empty")
}

This will evaluate to true and exit the application.
What can possibly make protobuf return empty string on MessageName?
I can see that the types and their names are registered as they should in the generated protobuf message code:

func init() {
	proto.RegisterType((*AddItemCommand)(nil), "messages.AddItemCommand")
	proto.RegisterType((*AddedItemEvent)(nil), "messages.AddedItemEvent")
	proto.RegisterType((*RenameCommand)(nil), "messages.RenameCommand")
	proto.RegisterType((*RenamedEvent)(nil), "messages.RenamedEvent")
	proto.RegisterType((*DumpCommand)(nil), "messages.DumpCommand")
}

I have also verified that the above init actually runs, and it does.

I have other generated proto messages, which returns their names as expected.
So what is going wrong here?

Edit
The protofile looks like this

syntax = "proto3";
package messages;

//user messages
message AddItemCommand {
  string item = 1;
}
message AddedItemEvent {
  string item = 1;
}
message RenameCommand {
  string name = 1;
}
message RenamedEvent {
  string name = 1;
}
message DumpCommand {}

Edit again.
Manually calling
proto.RegisterType((*messages.AddedItemEvent)(nil), "messages.AddedItemEvent")

From the main func, makes it work.
So somehow the type registry is cleared somewhere between the messages.init and main

Anyone?

答案1

得分: 1

不,类型注册表没有被清除。

很可能是因为您在生成的代码中使用了不同的proto包实现,其中proto包的实现选择由您选择的proto代码生成器来处理。在您的代码中,您实际上是在尝试获取MessageName

在Go中,每个导入的包在go二进制进程的生命周期内仅初始化一次。每个初始化的包都有自己的名称表(包括导出和未导出的名称)及其对应的值。特别是proto包维护了所谓的“注册表”,其中包含已知的proto消息名称和类型。简单来说,注册表是一个包范围的映射,将消息类型与其对应的名称关联起来。该映射在包初始化时进行初始化和填充。然后,要检索MessageName,您通过某个辅助函数间接访问它。关键点是,如果您在代码中使用了导入的不同proto包实现,或者在生成的代码中使用了不同的proto包实现,或者在依赖项中使用了不同的proto包实现,那么很有可能在应用程序启动时,您会填充一个注册表,然后稍后,您尝试从另一个不知道它的注册表中检索MessageName。经验法则是始终在整个应用程序中使用protoc代码生成器插件、预生成的proto类型库和来自单个供应商的proto包实现。

最流行的proto包(我从未使用或遇到其他包)是https://github.com/golang/protobuf和https://github.com/gogo/protobuf。
请仔细检查您的protoc生成器插件和包导入,希望一切都会好起来。

英文:

No, type registry isn't cleared.

Most probably you are using different implementations of proto package in your generated code, where proto package implementation choice is addressed by your proto code generator of choice. In your code you are actually trying to get MessageName.

Each imported package in Go is initialized exactly one time during the go binary process lifetime. Each initialized package has it's own table of names (both exported and unexported) and their corresponding values. proto package in particular maintains so called "registry" of known proto message names and types. A bit simplified, registry is a package-scoped map associating message types with their corresponding names. This map is initialized and populated on package initialization. Then, to retrieve MessageName you access it indirectly through some helper function. The key point is, if you use different proto package implementations imported in your code, your generated code, or in your dependencies, then chances are, on app start you populate one registry, and then later on, you are trying to retrieve MessageName from some other registry that know nothing about it. The rule of thumb is to always use protoc code generator plugin, pregenerated proto type libraries, and proto package implementation from a single vendor through out your app.

Most popular proto packages (I've never used or encountered any other) are https://github.com/golang/protobuf and https://github.com/gogo/protobuf.
Double check your protoc generator plugin and package imports and I hope it'll be all fine.

huangapple
  • 本文由 发表于 2016年9月27日 05:44:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/39712915.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定