为什么 foo.pb.go 默认实现使用全局注册表,我可以修改它以避免名称空间冲突吗?

问题描述 投票:0回答:1

来自官方文档说。如果我有一个 proto 文件并使用它在不同的 pkg 中生成两个 pb,它将遇到命名空间冲突。例如,example.proto 分别生成为 pkg1 和 pkg2 中的两个 example.pb.go。

├─test_proto
    ├─main.go
    ├─example.proto
    ├─pkg1
       ├─example
           ├─example.pb.go
    ├─pkg2
       ├─example
           ├─example.pb.go

示例.proto

syntax = "proto3";

package example;
option go_package = "./example";

message Greeting {
  string first_name = 1;
  string last_name = 2;
}

我使用的命令:

protoc --go_out=./pkg1 -I=. example.proto
protoc --go_out=./pkg2 -I=. example.proto

main.go

package main

import (
    "fmt"

    "Hello/test_proto/pkg1/example"
    pkg2proto "Hello/test_proto/pkg2/example"
)

func main() {
    pkg1Greeting := example.Greeting{FirstName: "foo"}
    pkg2Greeting := pkg2proto.Greeting{FirstName: "bar"}

    fmt.Println(pkg1Greeting.FirstName, pkg2Greeting.FirstName)
}

现在如果我运行 main.go。它会因命名空间冲突而恐慌。

Exception has occurred: panic
"proto:\u00a0file \"example.proto\" is already registered\n\tprevious...

但是如果我修改 example.pb.go 之一:

import "google.golang.org/protobuf/reflect/protoregistry"

    out := protoimpl.TypeBuilder{
        File: protoimpl.DescBuilder{
            GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
            RawDescriptor: file_example_proto_rawDesc,
            NumEnums:      0,
            NumMessages:   1,
            NumExtensions: 0,
            NumServices:   0,
            FileRegistry: new(protoregistry.Files), // add this line
        },
        GoTypes:           file_example_proto_goTypes,
        DependencyIndexes: file_example_proto_depIdxs,
        MessageInfos:      file_example_proto_msgTypes,
        TypeRegistry: new(protoregistry.Types),    // add this line
    }.Build()

而是使用默认的全局 FileRegistry 和 TypeRegistry。如果我为它新建一个实例。程序将顺利运行。那么为什么默认实现必须使用全局Registry呢?我修改一下可以吗

go protocol-buffers protocols
1个回答
0
投票

由于文件以

开头
// Code generated by protoc-gen-go. DO NOT EDIT.

修改它可能不行;)

option go_package = "./example";

./example
不是有效的 go 包。应该是这样

option go_package = "Hello/test_proto/pkg2/example";

“Hello”是你的模块名称。如果你编辑生成的文件,你会严重搞乱 descriptorpb.DescriptorProtoprotoreflectAny

当需要不同的结构时,使用不同的文件。并在

option go_package
中使用有效的 Go 包名称。


编辑

生成代码中使用的包,

protoimpl
警告:“此包只能由生成的消息导入。除了保持现有生成的消息可运行所需的功能之外,兼容性协议不涵盖任何其他内容。由于未经授权使用此软件包而造成的损坏不属于作者的责任。”

您可以使用自己的注册表,但不能使用生成的代码。例如

any.UnmarshalNew
“UnmarshalNew 使用全局类型注册表来解析消息类型并构造要解组到的该消息的新实例。”

proto.UnmarshalOptions
“解析器用于在解组扩展字段时查找类型。如果为零,默认使用 protoregistry.GlobalTypes。”

由于这是递归调用的,因此您不能在注册表之间混合类型,并且必须在每次调用时提供自己的注册表。

我预计这种破坏一开始会很微妙,但你显然违反了生成代码所做的假设。

另请参阅 “protoregistry.GlobalTypes 如何加载类型?”,了解全局注册表中缺少类型时出现的奇怪结果。

© www.soinside.com 2019 - 2024. All rights reserved.