尝试编写共享的protobuf定义。

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

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应该是这样的:

  1. import (
  2. "github.com/rob-woerner/protos/pb/common" // /pb/
  3. ...
  4. )

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

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

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

  • example.proto
  1. syntax = "proto3";
  2. package baz;
  3. option go_package = "github.com/foo/protos;baz";
  4. service Some {...}

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

然后:

  1. MODULE="github.com/foo/protos"
  2. protoc \
  3. --include_imports \
  4. --include_source_info \
  5. --proto_path=. \
  6. --descriptor_set_out=foo.pb \
  7. --go_out=./some/baz \
  8. --go_opt=module=${MODULE} \
  9. --go-grpc_out=./some/baz \
  10. --go-grpc_opt=module=${MODULE} \
  11. ./*.proto

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

这将产生以下结果:

  1. .
  2. ├── example.proto
  3. ├── some
  4. └── baz
  5. ├── example_grpc.pb.go
  6. └── example.pb.go
  7. ├── go.mod
  8. ├── go.sum
  9. └── protoc-3.17.3-linux-x86_64

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

go.mod:

  1. module github.com/bar/app
  2. go 1.16
  3. require (
  4. github.com/foo/protos v0.0.1
  5. ...
  6. )

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

  1. package main
  2. import (
  3. ...
  4. pb "github.com/foo/protos/some/baz"
  5. )
  6. func main() {
  7. ...
  8. pb.RegisterSomeServer(grpcServer, ...)

更新

注意 回应评论线程

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

我们来做两个包:

common.proto:

  1. syntax = "proto3";
  2. package common;
  3. option go_package = "github.com/rob-woerner/protos/common;common";
  4. enum MyState { LOGGED_OUT = 0; }

messages.proto:

  1. syntax = "proto3";
  2. package messages;
  3. import "common.proto";
  4. option go_package = "github.com/rob-woerner/protos/messages;messages";
  5. message MyRequest { common.MyState state = 1; }

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

然后:

  1. MODULE="github.com/rob-woerner/protos"
  2. protoc \
  3. --proto_path=. \
  4. --go_out=./some \
  5. --go_opt=module=${MODULE} \
  6. --go-grpc_out=./some \
  7. --go-grpc_opt=module=${MODULE}

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

结果为:

  1. some
  2. ├── common
  3. └── common.pb.go
  4. └── messages
  5. └── messages.pb.go

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

现在,我们可以:

main.go:

  1. package main
  2. import (
  3. "github.com/rob-woerner/protos/some/common"
  4. "github.com/rob-woerner/protos/some/messages"
  5. )
  6. func main() {
  7. rqst := messages.MyRequest{
  8. state: common.MyState_LOGGED_OUT,
  9. }
  10. // 对`rqst`做一些操作
  11. }

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

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

然后:

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

然后:

  1. MODULE="github.com/rob-woerner/protos"
  2. protoc \
  3. --proto_path=. \
  4. --go_out=./protos \
  5. --go_opt=module=${MODULE} \
  6. --go-grpc_out=./protos \
  7. --go-grpc_opt=module=${MODULE}

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

main.go:

  1. package main
  2. import (
  3. pb "github.com/rob-woerner/protos"
  4. )
  5. func main() {
  6. rqst := pb.MyRequest{
  7. state: pb.MyState_LOGGED_OUT,
  8. }
  9. // 对`rqst`做一些操作
  10. }

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

英文:

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:

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

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
  1. syntax = &quot;proto3&quot;;
  2. package baz;
  3. option go_package = &quot;github.com/foo/protos;baz&quot;;
  4. 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:

  1. MODULE=&quot;github.com/foo/protos&quot;
  2. protoc \
  3. --include_imports \
  4. --include_source_info \
  5. --proto_path=. \
  6. --descriptor_set_out=foo.pb \
  7. --go_out=./some/baz \
  8. --go_opt=module=${MODULE} \
  9. --go-grpc_out=./some/baz \
  10. --go-grpc_opt=module=${MODULE} \
  11. ./*.proto

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

This will yield:

  1. .
  2. ├── example.proto
  3. ├── some
  4. │&#160;&#160; └── baz
  5. │&#160;&#160; ├── example_grpc.pb.go
  6. │&#160;&#160; └── example.pb.go
  7. ├── go.mod
  8. ├── go.sum
  9. └── protoc-3.17.3-linux-x86_64

Then, from bar/app, I have:

go.mod:

  1. module github.com/bar/app
  2. go 1.16
  3. require (
  4. github.com/foo/protos v0.0.1
  5. ...
  6. )

And, within that module, I alias the imports:

  1. package main
  2. import (
  3. ...
  4. pb &quot;github.com/foo/protos/some/baz&quot;
  5. )
  6. func main() {
  7. ...
  8. 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:

  1. syntax = &quot;proto3&quot;;
  2. package common;
  3. option go_package = &quot;github.com/rob-woerner/protos/common;common&quot;;
  4. enum MyState { LOGGED_OUT = 0; }

messages.proto:

  1. syntax = &quot;proto3&quot;;
  2. package messages;
  3. import &quot;common.proto&quot;;
  4. option go_package = &quot;github.com/rob-woerner/protos/messages;messages&quot;;
  5. 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:

  1. MODULE=&quot;github.com/rob-woerner/protos&quot;
  2. protoc \
  3. --proto_path=. \
  4. --go_out=./some \
  5. --go_opt=module=${MODULE} \
  6. --go-grpc_out=./some \
  7. --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:

  1. some
  2. ├── common
  3. │&#160;&#160; └── common.pb.go
  4. └── messages
  5. └── 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:

  1. package main
  2. import (
  3. &quot;github.com/rob-woerner/protos/some/common&quot;
  4. &quot;github.com/rob-woerner/protos/some/messages&quot;
  5. )
  6. func main() {
  7. rqst := messages.MyRequest{
  8. state: common.MyState_LOGGED_OUT,
  9. }
  10. // do something with `rqst`
  11. }

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

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

And:

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

And then:

  1. MODULE=&quot;github.com/rob-woerner/protos&quot;
  2. protoc \
  3. --proto_path=. \
  4. --go_out=./protos \
  5. --go_opt=module=${MODULE} \
  6. --go-grpc_out=./protos \
  7. --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:

  1. package main
  2. import (
  3. pb &quot;github.com/rob-woerner/protos&quot;
  4. )
  5. func main() {
  6. rqst := pb.MyRequest{
  7. state: pb.MyState_LOGGED_OUT,
  8. }
  9. // do something with `rqst`
  10. }

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:

确定