当前位置:网站首页>【protobuf 】protobuf 昇級後帶來的一些坑
【protobuf 】protobuf 昇級後帶來的一些坑
2022-06-26 20:57:00 【碼農印象】
前段時間把公司某項目依賴的 github.com/golang/protobuf
的版本從 v1.3.3 昇級到了 v1.4.2,本文記錄了昇級過程中遇到的一些問題。
Google 對 Go 的 protobuf 庫的底層進行了大的改進,新版本的包路徑轉移到了 google.golang.org/protobuf
.
同時,這些改進也被帶進了 github.com/golang/protobuf
:從 v1.4
版本起,github.com/golang/protobuf
會在 google.golang.org/protobuf
的基礎上實現,但會保證接口兼容,這也錶明當前依賴 github.com/golang/protobuf
的項目可以直接昇級版本,而無需對上層代碼進行改動。
然而,新版的 protobuf-gen-go
使用了 google.golang.org/protobuf/protoreflect
,編譯出的 message 結構體與之前完全不同,這給我們的昇級工作帶來了一些麻煩。
1. 代碼中對 XXX_Unmarshal
的直接調用
老版的 protoc-gen-go
會暴露一個 XXX_Unmarshal
接口,用於在 proto.Unmarshal
時進行調用,所以有一些同事選擇會直接調用 message.XXX_Unmarshal
方法。新版的 proto 通過 ProtoReflect
接口暴露 message 內部信息,編譯 pb.go
時也沒有了 XXX_Unmarshal
方法,所以會導致編譯時報錯 message.XXX_Unmarshal undefined
.
解决方案很簡單:改用 proto.Unmarshal
即可。
2. 結構體內部結構變化導致測試出錯
針對同一個 message,老版本編譯後的結構體結構如下:
type HealthCheckResponse struct {
Status HealthCheckResponse_ServingStatus `protobuf:"varint,1,opt,name=status,proto3,enum=liulishuo.common.health.v1.HealthCheckResponse_ServingStatus" json:"status,omitempty"`
XXX_NoUnkeyedLiteral struct{
} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
而新版本編譯後的結構如下:
type HealthCheckResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Status HealthCheckResponse_ServingStatus `protobuf:"varint,1,opt,name=status,proto3,enum=liulishuo.common.health.v1.HealthCheckResponse_ServingStatus" json:"status,omitempty"`
}
可以看到,新版本中添加了三個未導出字段,而這三個字段為我們的測試代碼帶來了一些麻煩。
- cmp.Equal 時 panic
我們的測試中使用了github.com/google/go-cmp/cmp.Equal
來對 proto 結構體進行比較,而結構體中的未導出字段會讓cmp.Equal
和cmp.Diff
panic:
panic: cannot handle unexported field at {
*pkg.SomeRequest}.state:
".../services_go_proto".SomeRequest
consider using a custom Comparer; if you control the implementation of type, you can also consider using an Exporter, AllowUnexported, or cmpopts.IgnoreUnexported [recovered]
go-cmp
推薦的方式是使用 IgnoreUnexported
,但這種方式需要傳遞所有需要忽略的類型,對含有多層嵌套的 message 非常不友好。
經過一番搜索,發現 protocmp.Transform
可以將所有的 protobuf message 轉換成自定義的 map[string]interface{}
進行比較,所以我們可以用 Transform()
來解决問題:
import "google.golang.org/protobuf/testing/protocmp"
// ...
opt := protocomp.Trnasform()
if !cmp.Equal(exp, got, opt) {
t.Error(exp, got, opt)
}
- assert 卡死並占滿內存
相比上面的問題,下面的問題更加奇怪:使用github.com/stretchr/testify/assert.Equal
比較某些特殊的 proto message 時會卡死,同時內存占用會暴漲。
嘗試用 pprof 取樣,取出來的 CPU 和堆采樣圖長這樣:
可以看到 spew.Dump
中存在無限遞歸,這導致了程序卡死以及持續的內存分配。
隨後搜到了testify 的 issue,相關評論中提出了幾種繞過的方案,然而這個問題至今沒有解决。
個人推薦的解决方式有兩種:
- 使用
marshalTextString()
將 message 轉換成 proto text,然後再進行比較; - 使用
cmp.Equal
,結合protocmp.Transform
.
3. lint 報錯 copylocks
處理完業務代碼處理測試,處理完測試代碼還有 lint 要處理。
我們的項目在昇級完後,go vet 會報 copylocks 錯誤:assignment copies lock value to got: .../message_go_protos.Message contains google.golang.org/protobuf/internal/impl.MessageState contains sync.Mutex
解决方式也比較簡單:將所有 proto message 改為指針傳遞即可。
边栏推荐
- On the origin of the dispute between the tradition and the future of database -- AWS series column
- Daily basic use of alicloud personal image warehouse
- 分布式ID生成系统
- 大家都能看得懂的源码(一)ahooks 整体架构篇
- Leetcode: hash table 08 (sum of four numbers)
- C language 99 multiplication table
- Guomingyu: Apple's AR / MR head mounted display is the most complicated product in its history and will be released in January 2023
- c语言99乘法表
- 710. random numbers in the blacklist
- 515. find the maximum value in each tree row
猜你喜欢
这些地区考研太疯狂!哪个地区报考人数最多?
leetcode刷题:字符串01(反转字符串)
飞天+CIPU体为元宇宙带来更大想象空间
Gee: calculate the maximum and minimum values of pixels in the image area
超分之VRT
Unity——Mathf. Similarities and differences between atan and atan2
后台查找,如何查找网站后台
Distributed ID generation system
回首望月
Some cold knowledge about QT database development
随机推荐
Disruptor本地线程队列_使用transProcessor处理器和WorkPool两种方式进行消费对比---线程间通信工作笔记005
证券开户安全吗,有没有什么危险呢
Serial port application program based on gd32
Stop being a giant baby
孙老师版本JDBC(2022年6月12日21:34:25)
超分之VRT
【最详细】最新最全Redis面试大全(70道)
leetcode刷题:字符串02( 反转字符串II)
710. random numbers in the blacklist
Sentinelresource annotation details
Gee: calculate the maximum and minimum values of pixels in the image area
JWT operation tool class sharing
手机股票注册开户有没有什么风险?安全吗?
C: Reverse linked list
阿里云个人镜像仓库日常基本使用
Sword finger offer II 091 Paint the house
SentinelResource注解詳解
Leetcode question brushing: String 05 (Sword finger offer 58 - ii. left rotation string)
JWT操作工具类分享
动态规划111