V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
newmiao
V2EX  ›  Go 编程语言

记一次奇妙的 go-protobuf 包升级之旅

  •  1
     
  •   newmiao · 2020-04-18 17:13:14 +08:00 · 2755 次点击
    这是一个创建于 1665 天前的主题,其中的信息可能已经有所发展或是发生改变。

    今天聊一个最近升级 go 的protobuf的故事。过程很是奇妙(曲折)😳

    前两天,一个项目的dependabot提示包github.com/golang/protobuf 可以从V1.3.5升级到V1.4.0

    Round One

    本以为直接升级就行,但是没过 CI,是发现旧版(V1.3.5)测试代码用了 pb 生成代码的XXX_Size()方法计算消息大小

    在新版(v1.4.0)里panic

    我们来看下他们有啥不同:

    为简化,我们 proto 文件用官方的helloworld.proto

    通过以下方式生成V1.3.5版本的 pb 文件

    curl -O https://raw.githubusercontent.com/grpc/grpc-go/master/examples/helloworld/helloworld/helloworld.proto
    brew install protobuf
    GO111MODULE=on go get -u github.com/golang/protobuf/[email protected]
    protoc  --go_out=plugins=grpc:. helloworld.proto
    

    再替换github.com/golang/protobuf/[email protected],生成新版 pb 文件

    查找XXX_Size函数

    旧版中没问题

    // helloword.pb.go
    func (m *HelloRequest) XXX_Size() int {
      return xxx_messageInfo_HelloRequest.Size(m)
    }
    var xxx_messageInfo_HelloRequest proto.InternalMessageInfo
    
    // github.com/golang/[email protected]/proto/table_marshal.go
    func (a *InternalMessageInfo) Size(msg Message) int {
    

    新版 pb 文件中没有了InternalMessageInfo类型

    但源码中能找到,是被废弃了。

    // github.com/golang/[email protected]/proto/deprecated.go
    // Deprecated: Do not use.
    type InternalMessageInfo struct{}
    
    func (*InternalMessageInfo) Size(Message) int { panic("not implemented") }
    // 同样废弃的还有:DiscardUnknown, Marshal, Merge, Unmarshal
    

    那新版代码中怎么获取大小?

    查看源码中例子,proto.Size(m Message) int可以实现

    InternalMessageInfo也变成了接口type Message = protoiface.MessageV1

    这个InternalMessageInfo的废弃在版本升级中也提到了,详见generated-code

    好,替换方法,第一回合结束

    准备收工

    Round Two

    等等,再仔细看了下github.com/golang/protobuf文档,里边提到v1.4.0版本后, 后边代码将交由google.golang.org/protobuf Repo 维护,看来谷歌要把 pb 的包都收到自己组织下

    那我顺手直接把grpc生成工具也替换过去呗!

    GO111MODULE=on go get -u google.golang.org/protobuf/cmd/[email protected]
    

    再次生成代码

    新工具自动提示要求 proto 文件中增加:

    option go_package = ".;helloworld";

    居然失败了。。。提示:

    --go_out: protoc-gen-go: plugins are not supported; use 'protoc --go-grpc_out=...' to generate gRPC
    

    难道是新增了 flag,尝试:

    protoc  --go-grpc_out=. helloworld.proto
    

    What? 失败+1 。。。

    protoc-gen-go-grpc: program not found or is not executable
    Please specify a program using absolute path or make sure the program is available in your PATH system variable
    --go-grpc_out: protoc-gen-go-grpc: Plugin failed with status code 1.
    

    心塞 x1

    protoc-gen-go-grpc是新工具?

    搜了下,确实有这个工具,说新版本会用他来生成grpc,主要是为了更好支持protobuf reflection

    这里说一下:

    proto 文件中的service是需要grpcplugin才能生成对应 pb 代码 所以旧版工具有参数--go_out=plugins=grpc:

    尝试安装下:

    GO111MODULE=on go get google.golang.org/protobuf/cmd/protoc-gen-go-grpc
    

    失败+2 。。。

    go get google.golang.org/protobuf/cmd/protoc-gen-go-grpc: module google.golang.org/protobuf@upgrade found (v1.21.0), but does not contain package google.golang.org/protobuf/cmd/protoc-gen-go-grpc
    

    心塞 x2

    goDoc 里有protoc-gen-go-grpcRepo里没有,这是什么操作?

    猜测这问题应该有人遇到吧,果然搜到了 issue:plugins are not supported:grpc

    原来是google.golang.org/protobuf先发布了,里边也包含了新版的protoc-gen-go

    只是其不再支持grpc生成,需要另一个工具protoc-gen-go-grpc

    然而它还没有发布,还在Review中。。。(当然目前也不会是稳定版本)

    令人窒息的操作

    不过,官方也说了,依然可以用旧包github.com/golang/protobufprotoc-gen-go-grpc工具生成grpc代码

    因为旧包protoc-gen-go-grpc里依然可以用新包protoc-gen-go-grpc里生成 grpc 代码的gengogrpc, 见comment

    Round Three

    好吧,那就只升级代码中调用的 protobuf 为google.golang.org/[email protected],代码生成工具还用旧版里github.com/golang/protobuf/[email protected]

    再次生成 pb 文件, 终于没有问题了, peace finally

    peace

    proto "github.com/golang/protobuf/proto"
    grpc "google.golang.org/grpc"
    codes "google.golang.org/grpc/codes"
    status "google.golang.org/grpc/status"
    protoreflect "google.golang.org/protobuf/reflect/protoreflect"
    protoimpl "google.golang.org/protobuf/runtime/protoimpl"
    

    只是看着生成代码里的依然需要import的旧包github.com/golang/protobuf,总感觉哪里怪怪的

    升级完了,却依赖了两种protobuf包。。。

    最后,劝大家不着急就再等等再升级吧

    (另外没事干升级到新包干什么!)

    当然这次 protobuf 的 breaking change 还是很有意义的,不仅让将 protobuf 反射作为 pb 的一级功能,还提供了很多处理工具,详细看下: v1.21.0-release


    文章首发公众号:newbmiao

    推荐阅读:Dig101-Go 系列

    欢迎关注,获取及时更新内容

    3 条回复    2020-04-18 22:46:49 +08:00
    eslizn
        1
    eslizn  
       2020-04-18 17:28:41 +08:00
    不想升级依赖到最新版本的开发不是好开发🐶
    May725
        2
    May725  
       2020-04-18 20:19:22 +08:00 via iPhone
    瞎折腾🤣
    newmiao
        3
    newmiao  
    OP
       2020-04-18 22:46:49 +08:00
    没办法,项目没有历史包袱,要求时常更新。。。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   993 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 22:10 · PVG 06:10 · LAX 14:10 · JFK 17:10
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.