【问题标题】:How to don't repeat the same fields for each proto structure?如何不为每个原型结构重复相同的字段?
【发布时间】:2021-04-18 19:46:37
【问题描述】:

我是第一次使用 protobuf .proto 文件。

我有许多具有相同字段的模型:

message Player {
  uint64 id = 1;
  google.protobuf.Timestamp createdAt = 2;
  google.protobuf.Timestamp updatedAt = 3;
  string firstname = 4;
  string lastname = 5;
  //...
}
message Team {
  uint64 id = 1;
  google.protobuf.Timestamp createdAt = 2;
  google.protobuf.Timestamp updatedAt = 3;
  string name = 4;
  //...
}
message League {
  uint64 id = 1;
  google.protobuf.Timestamp createdAt = 2;
  google.protobuf.Timestamp updatedAt = 3;
  string name = 4;
  //...
}

...还有许多其他...

如您所见,我有在每个结构中重复相同的字段

在这种情况下,DRY(不要重复自己)的最佳做法是什么?

我正在使用 Golang。

我可以像 Go 语言一样嵌入它们吗?

【问题讨论】:

  • 您可以创建带有公共字段的消息,并使用它。但是,对于这些情况,重复一遍通常更具可读性。
  • 你能告诉我怎么做吗?

标签: go data-structures protocol-buffers proto


【解决方案1】:

您可以使用公共字段创建消息:

message Meta {
  uint64 id=1;
  google.protobuf.Timestamp createdAt = 2;
  google.protobuf.Timestamp updatedAt = 3;
}

message Player {
  Meta meta=1;
  string firstname = 4;
  string lastname = 5;
  //...
}

message Team {
  Meta id = 1;
  string name = 2;
  //...
}
message League {
  Meta meta=1;
  string name = 2;
  //...
}

【讨论】:

  • 您认为这会减慢编码/解码过程吗?专门用Go?
  • 您可以测量和查看。与网络速度相比,它可能并不重要。
【解决方案2】:

您可以简单地使用经常重复的字段定义一条新消息,并将其用作其他消息中的字段类型:

message Metadata {
  uint64 id = 1;
  google.protobuf.Timestamp createdAt = 2;
  google.protobuf.Timestamp updatedAt = 3;
}

message League {
  Metadata metadata = 1;
  string name = 2;
  //...
}

在 Go 中,您将把 Metadata 字段初始化为普通结构(包选择器将取决于您实际从 protobuffers 生成 Go 类型的方式):

func newLeague() *grpcgen.League {
    return &grpcgen.League{
       Metadata: &grpcgen.Metadata{
           Id:        1000,
           CreatedAt: ptypes.TimestampProto(time.Now()),
           UpdatedAt: ptypes.TimestampProto(time.Now()),
       }
       Name: "foo",
       //...
    }
}

编辑:

你认为这会减慢编码/解码过程

我们可以运行以下基准测试:

package foo

import (
    "github.com/golang/protobuf/proto"
    "github.com/golang/protobuf/ptypes"
    "foo/grpcgen"
    "testing"
)

var withmeta = &grpcgen.LeagueWithMeta{
    Metadata: &grpcgen.Metadata{
        Id: 1000,
        CreatedAt: ptypes.TimestampNow(),
        UpdatedAt: ptypes.TimestampNow(),
    },
    Name: "foo",
}

var nometa = &grpcgen.LeagueNoMeta{
    Id: 1000,
    CreatedAt: ptypes.TimestampNow(),
    UpdatedAt: ptypes.TimestampNow(),
    Name: "foo",
}

func BenchmarkEncProto(b *testing.B) {
    b.Run("encode with meta", func(b *testing.B) {
        b.ReportAllocs()
        for i := 0; i < b.N; i++ {
            b, _ := proto.Marshal(withmeta)
            if b == nil {
                panic("not marshaled")
            }
        }
    })

    b.Run("encode without meta", func(b *testing.B) {
        b.ReportAllocs()
        for i := 0; i < b.N; i++ {
            b, _ := proto.Marshal(nometa)
            if b == nil {
                panic("not marshaled")
            }
        }
    })
}
$ go test -bench=. ./proto_benchmark_test.go -benchtime=10s
goos: darwin
goarch: amd64
cpu: Intel(R) Core(TM) i7-7660U CPU @ 2.50GHz
BenchmarkEncProto/encode_with_meta-4            13124650           948.8 ns/op        96 B/op          4 allocs/op
BenchmarkEncProto/encode_without_meta-4         25832161           417.0 ns/op        64 B/op          2 allocs/op
PASS
ok      command-line-arguments  24.629s

将字段包装在Metadata 消息中具有更好的性能,这并不奇怪。实际上,这不会以明显的方式影响您的程序。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-31
    • 2019-05-20
    • 1970-01-01
    • 1970-01-01
    • 2021-08-14
    • 1970-01-01
    相关资源
    最近更新 更多