記一次奇妙的go-protobuf包升級之旅

今天聊一個最近升級go的protobuf的故事。過程非常奇妙(曲折)😳git

前兩天,一個項目的dependabot提示包github.com/golang/protobuf 能夠從V1.3.5升級到V1.4.0github

Round One

本覺得直接升級就行,可是沒過CI,是發現舊版(V1.3.5)測試代碼用了pb生成代碼的XXX_Size()方法計算消息大小golang

在新版(v1.4.0)裏panicshell

咱們來看下他們有啥不一樣:curl

爲簡化,咱們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/protoc-gen-go@v1.3.5
protoc  --go_out=plugins=grpc:. helloworld.proto
複製代碼

再替換github.com/golang/protobuf/protoc-gen-go@v1.4.0,生成新版pb文件測試

查找XXX_Size函數ui

舊版中沒問題google

// helloword.pb.go
func (m *HelloRequest) XXX_Size() int {
  return xxx_messageInfo_HelloRequest.Size(m)
}
var xxx_messageInfo_HelloRequest proto.InternalMessageInfo

// github.com/golang/protobuf@v1.3.5/proto/table_marshal.go
func (a *InternalMessageInfo) Size(msg Message) int {
複製代碼

新版pb文件中沒有了InternalMessageInfo類型

但源碼中能找到,是被廢棄了。

// github.com/golang/protobuf@v1.4.0/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/protoc-gen-go@v1.21.0
複製代碼

再次生成代碼

新工具自動提示要求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/protobuf@v1.21.0,代碼生成工具還用舊版裏github.com/golang/protobuf/protoc-gen-go@v1.4.0

再次生成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系列

歡迎關注,獲取及時更新內容
相關文章
相關標籤/搜索