尝试编写共享的protobuf定义。

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

Trying to write shared protobuf definitions

问题

我正在尝试在共享的Go模块中创建共享的protobuf定义,但是运气不太好。具体来说:

  • 我想在模块protos中有一个包含共享定义的proto文件。
  • 模块protos中的其他proto文件将引用这些共享定义。
  • 模块protos-use将通过go.mod引用模块protos。
  • 模块protos-use的Go代码将导入并使用模块protos中的定义。
  • 这两个模块都存储在github.com上。

我无法使各种包名称对齐。无论我使用什么,都会出现某种错误。目前,模块protos没有任何错误,但是在模块protos-use上运行go mod tidy会失败。

模块protos位于https://github.com/rob-woerner/protos。
模块protos-use位于https://github.com/rob-woerner/protos-use。

我还没有找到解决这种情况的示例。如果我简单地在各处复制共享的proto定义,就可以使其工作。最近的错误是:
$ go mod tidy
go: finding module for package github.com/rob-woerner/protos/messages
go: finding module for package github.com/rob-woerner/protos/common
github.com/rob-woerner/protos-use/code imports
github.com/rob-woerner/protos/common: module github.com/rob-woerner/protos@latest found (XXX), but does not contain package github.com/rob-woerner/protos/common
github.com/rob-woerner/protos-use/code imports
github.com/rob-woerner/protos/messages: module github.com/rob-woerner/protos@latest found (XXX), but does not contain package github.com/rob-woerner/protos/messages

但是我强调,我会得到很多不同的错误,这取决于需要包名称的十几个位置使用了什么包名称。

如果有人知道一个可行的示例,或者如果不支持这种情况,请告诉我。

为了简洁起见,省略了代码片段。

模块protos:

go.mod
Makefile
protobuf:
common.proto
messages.proto
pb:
common.pb.go
messages.pb.go

模块protos-use:
go.mod
code:
project.go

模块protos:
go.mod:

module protos
go 1.16
require google.golang.org/protobuf v1.27.1

protobuf/common.proto:
package common;
option go_package = "github.com/rob-woerner/protos";
enum MyState { LOGGED_OUT = 0; }

protobuf/messages.proto:
import "common.proto";
package messages;
option go_package = "github.com/rob-woerner/protos";
message MyRequest { common.MyState state = 1; }

Makefile:
protos:
protoc --proto_path=protobuf --go_out=plugins=grpc:pb --go_opt=module=github.com/rob-woerner/protos protobuf/*.proto

模块protos-use:

go.mod:
module github.com/rob-woerner/protos-use
go 1.16
require (
github.com/rob-woerner/protos latest
)

code/project.go:
package code
import (
"github.com/rob-woerner/protos/common"
"github.com/rob-woerner/protos/messages"
)
func MyFunc() messages.MyMessage {
return messages.MyMessage { state: common.MyState_LOGGED_OUT}
}

英文:

I'm trying to create shared protobuf definitions in a shared go module, but I'm not having much luck. Specifically:<br/>
- I want a proto file in module protos that contains shared definitions.<br/>
- Other proto files in module protos would reference the shared definitions.<br/>
- Module protos-use would reference module protos (via go.mod)<br/>
- Module protos-use go code would import and use definitions from module protos<br/>
- Both modules are stored in github.com

I'm unable to get the various package names to align. No matter what I use, something fails. Currently, module protos doesn't have any errors, but go mod tidy on module protos-use fails.

Module protos is at https://github.com/rob-woerner/protos&lt;br/>
Module protos-use is at https://github.com/rob-woerner/protos-use

I haven't found an example that addresses this scenario. If I simply duplicate the shared proto definitions everywhere, I can get it to work. The most recent error is:<br/>
$ go mod tidy<br/>
go: finding module for package github.com/rob-woerner/protos/messages<br/>
go: finding module for package github.com/rob-woerner/protos/common<br/>
github.com/rob-woerner/protos-use/code imports<br/>
github.com/rob-woerner/protos/common: module github.com/rob-woerner/protos@latest found (XXX), but does not contain package github.com/rob-woerner/protos/common<br/>
github.com/rob-woerner/protos-use/code imports<br/>
github.com/rob-woerner/protos/messages: module github.com/rob-woerner/protos@latest found (XXX), but does not contain package github.com/rob-woerner/protos/messages<br/>

But I emphasize that I get lots of different errors, depending on what package names I use in the dozen or so places that need package names.

If anyone knows of a working example, or if this is not supported, I'd appreciate it.

Code snippets are elided for compactness


Module protos:

go.mod<br/>
Makefile<br/>
protobuf:<br/>
&nbsp;&nbsp;&nbsp;common.proto<br/>
&nbsp;&nbsp;&nbsp;messages.proto<br/>
pb:<br/>
&nbsp;&nbsp;&nbsp;common.pb.go<br/>
&nbsp;&nbsp;&nbsp;messages.pb.go<br/>


Module protos-use:<br/>
go.mod<br/>
code:<br/>
&nbsp;&nbsp;&nbsp;project.go<br/>


Module protos:
go.mod:<br/><br/>
module protos<br/>
go 1.16<br/>
require google.golang.org/protobuf v1.27.1<br/>


protobuf/common.proto:<br/>
package common;<br/>
option go_package = "github.com/rob-woerner/protos";<br/>
enum MyState { LOGGED_OUT = 0; }<br/>


protobuf/messages.proto:<br/>
import "common.proto";<br/>
package messages;<br/>
option go_package = "github.com/rob-woerner/protos";<br/>
message MyRequest { common.MyState state = 1; }<br/>


Makefile:<br/>
protos:<br/>
&nbsp;&nbsp;&nbsp;protoc --proto_path=protobuf --go_out=plugins=grpc:pb --go_opt=module=github.com/rob-woerner/protos protobuf/*.proto<br/>


Module protos-use:

go.mod:<br/>
module github.com/rob-woerner/protos-use<br/>
go 1.16<br/>
require (<br/>
&nbsp;&nbsp;&nbsp;github.com/rob-woerner/protos latest<br/>
)<br/>


code/project.go:<br/>
package code<br/>
import (<br/>
&nbsp;&nbsp;&nbsp;"github.com/rob-woerner/protos/common"<br/>
&nbsp;&nbsp;&nbsp;"github.com/rob-woerner/protos/messages"<br/>
)<br/>
func MyFunc() messages.MyMessage {<br/>
&nbsp;&nbsp;&nbsp;return messages.MyMessage { state: common.MyState_LOGGED_OUT}<br/>
}<br/>

答案1

得分: 2

这在使用protobuf时确实具有挑战性,并且文档记录不完善。

我感觉其中一部分复杂性在于protobuf的维护者试图解决可能使用的各种运行时和包的问题。

如果我正确理解了你的问题,根据你的情况,protos-use遇到困难是因为code/product.go应该是这样的:

import (
    "github.com/rob-woerner/protos/pb/common" // /pb/
    ...
)

我有一个可行的机制,我将尝试将其应用到你的机制上。我认为对common的引用不是你的问题,而是整体结构和模块与包的导入:

  • github.com/foo/protos
  • github.com/bar/app

foo/protos中,我在根目录下有*.proto文件(但这只是一种约定,并不一定是必须的):

  • example.proto
syntax = "proto3";

package baz;

option go_package = "github.com/foo/protos;baz";

service Some {...}

注意 ;baz 描述了生成代码的最终Go包名称(baz)。它不必与仓库名称(protos)匹配,我不希望这样,因为(见下文),我将生成的代码放在一个非根目录(/some/baz)中。

然后:

MODULE="github.com/foo/protos"
protoc \
--include_imports \
--include_source_info \
--proto_path=. \
--descriptor_set_out=foo.pb \
--go_out=./some/baz \
--go_opt=module=${MODULE} \
--go-grpc_out=./some/baz \
--go-grpc_opt=module=${MODULE} \
./*.proto

注意 这将给我们一个包名为github.com/foo/protos/some/baz的包。

这将产生以下结果:

.
├── example.proto
├── some
│   └── baz
│       ├── example_grpc.pb.go
│       └── example.pb.go
├── go.mod
├── go.sum
└── protoc-3.17.3-linux-x86_64

然后,在bar/app中,我有:

go.mod:

module github.com/bar/app

go 1.16

require (
    github.com/foo/protos v0.0.1
    ...
)

并且,在该模块中,我为导入进行了别名:

package main

import (
    ...
    pb "github.com/foo/protos/some/baz"
)

func main() {
    ...
    pb.RegisterSomeServer(grpcServer, ...)

更新

注意 回应评论线程

commonmessages可以在一个包中或者两个包中。

我们来做两个包:

common.proto:

syntax = "proto3";

package common;

option go_package = "github.com/rob-woerner/protos/common;common";

enum MyState { LOGGED_OUT = 0; }

messages.proto:

syntax = "proto3";

package messages;

import "common.proto";

option go_package = "github.com/rob-woerner/protos/messages;messages";

message MyRequest { common.MyState state = 1; }

注意 因为我们想要两个包,我们需要给protos(!)唯一的packagego_package名称./protos/common;common。虽然我们可以使用./protos/common;freddie,但问题是路径必须是唯一的,以提供唯一的包。

然后:

MODULE="github.com/rob-woerner/protos"

protoc \
--proto_path=. \
--go_out=./some \
--go_opt=module=${MODULE} \
--go-grpc_out=./some \
--go-grpc_opt=module=${MODULE}

注意 这里使用some仅仅是为了确保生成的代码最终聚合在某个包(some)下面,而不是仓库(==模块)的根目录。如果你希望包直接位于模块根目录下,可以删除/some

结果为:

some
├── common
│   └── common.pb.go
└── messages
    └── messages.pb.go

注意 some成为根包github.com/rob-woerner/protos/somecommon.pb.go位于子包github.com/rob-woerner/protos/some/common中。

现在,我们可以:

main.go:

package main

import (
    "github.com/rob-woerner/protos/some/common"
    "github.com/rob-woerner/protos/some/messages"
)

func main() {
    rqst := messages.MyRequest{
        state: common.MyState_LOGGED_OUT,
    }
    // 对`rqst`做一些操作
}

如果你希望commonmessages在同一个包中,那么你可以简单地:

package protos;

option go_package = "github.com/rob-woerner/protos;protos";

然后:

message MyRequest { MyState state = 1; } // 现在在同一个包中

然后:

MODULE="github.com/rob-woerner/protos"

protoc \
--proto_path=. \
--go_out=./protos \
--go_opt=module=${MODULE} \
--go-grpc_out=./protos \
--go-grpc_opt=module=${MODULE}

注意 在这种情况下,我们需要_out=./protos,以便将生成的文件放在正确的包(protos)下面。

main.go:

package main

import (
    pb "github.com/rob-woerner/protos"
)

func main() {
    rqst := pb.MyRequest{
        state: pb.MyState_LOGGED_OUT,
    }
    // 对`rqst`做一些操作
}

哇!我说过这很具有挑战性;-)

英文:

This is challenging with protobufs and is poorly documented.

Part of the complexity -- I sense -- is that the protobuf maintainers are trying to solve for the myriad runtimes and packages that may be used.

If I understand your problem correctly, with what you have, protos-use is struggling because code/product.go should:

import (
    &quot;github.com/rob-woerner/protos/pb/common&quot; // /pb/
    ...
)

I've got a working mechanism that I'll try to apply to your mechanism. I think the references to common aren't your issue but the overall structure and modules vs. packages imports:

  • github.com/foo/protos
  • github.com/bar/app

In foo/protos, I have *.proto files in the root (but this is just a convention and need not be the case):

  • example.proto
syntax = &quot;proto3&quot;;

package baz;

option go_package = &quot;github.com/foo/protos;baz&quot;;

service Some {...}

> NOTE The ;baz this describe the eventual Go package name (baz) of the generated code. It need not match the the repo name (protos) and I don't want this because (see below), I'm putting the generated code in a non-root directory (/some/baz)

Then:

MODULE=&quot;github.com/foo/protos&quot;
protoc \
--include_imports \
--include_source_info \
--proto_path=. \
--descriptor_set_out=foo.pb \
--go_out=./some/baz \
--go_opt=module=${MODULE} \
--go-grpc_out=./some/baz \
--go-grpc_opt=module=${MODULE} \
./*.proto

> NOTE This will give us a package name of github.com/foo/protos/some/baz

This will yield:

.
├── example.proto
├── some
│&#160;&#160; └── baz
│&#160;&#160;     ├── example_grpc.pb.go
│&#160;&#160;     └── example.pb.go
├── go.mod
├── go.sum
└── protoc-3.17.3-linux-x86_64

Then, from bar/app, I have:

go.mod:

module github.com/bar/app

go 1.16

require (
    github.com/foo/protos v0.0.1
    ...
)

And, within that module, I alias the imports:

package main

import (
    ...
	pb &quot;github.com/foo/protos/some/baz&quot;
)

func main() {
    ...
    pb.RegisterSomeServer(grpcServer, ...)

Update

> NOTE Responding to the comment thread

common and messages are either in one package or in two.

Let's do two:

common.proto:

syntax = &quot;proto3&quot;;

package common;

option go_package = &quot;github.com/rob-woerner/protos/common;common&quot;;

enum MyState { LOGGED_OUT = 0; }

messages.proto:

syntax = &quot;proto3&quot;;

package messages;

import &quot;common.proto&quot;;

option go_package = &quot;github.com/rob-woerner/protos/messages;messages&quot;;

message MyRequest { common.MyState state = 1; }

> NOTE Because we want 2 packages, we need to give the protos (!) unique package, go_package names ./protos/common;common. Although we could have ./protos/common;freddie, the issue is that the path must be unique to give a unique package.

Then:

MODULE=&quot;github.com/rob-woerner/protos&quot;

protoc \
--proto_path=. \
--go_out=./some \
--go_opt=module=${MODULE} \
--go-grpc_out=./some \
--go-grpc_opt=module=${MODULE}

> NOTE The use of some here is solely to ensure that the generated code ends up aggregated by some package (some) below the repo(==module) root. You can remove /some if you want the packages directly beneath the module root.

yields:

some
├── common
│&#160;&#160; └── common.pb.go
└── messages
    └── messages.pb.go

> NOTE some becomes the root package github.com/rob-woerner/protos/some and common.pb.go is in the sub-package github.com/rob-woerner/protos/some/common

So, we can now:

main.go:

package main

import (
	&quot;github.com/rob-woerner/protos/some/common&quot;
	&quot;github.com/rob-woerner/protos/some/messages&quot;
)

func main() {
	rqst := messages.MyRequest{
		state: common.MyState_LOGGED_OUT,
	}
    // do something with `rqst`
}

If you want common and messages to be in the same package, then you can just:

package protos;

option go_package = &quot;github.com/rob-woerner/protos;protos`;

And:

message MyRequest { MyState state = 1; } // Now in same package

And then:

MODULE=&quot;github.com/rob-woerner/protos&quot;

protoc \
--proto_path=. \
--go_out=./protos \
--go_opt=module=${MODULE} \
--go-grpc_out=./protos \
--go-grpc_opt=module=${MODULE}

> NOTE In this case, we need _out=./protos so that the generated files get put under the correct package (protos

main.go:

package main

import (
    pb &quot;github.com/rob-woerner/protos&quot;
)

func main() {
    rqst := pb.MyRequest{
        state: pb.MyState_LOGGED_OUT,
    }
    // do something with `rqst`
}

Phew! I said it is challenging 尝试编写共享的protobuf定义。

huangapple
  • 本文由 发表于 2021年8月3日 03:54:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/68627269.html
匿名

发表评论

匿名网友

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

确定