当前位置:网站首页>【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.Diffpanic:
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 改為指針傳遞即可。
边栏推荐
- Guomingyu: Apple's AR / MR head mounted display is the most complicated product in its history and will be released in January 2023
- 分布式ID生成系统
- Three basic backup methods of mongodb
- 孙老师版本JDBC(2022年6月12日21:34:25)
- StringUtils判断字符串是否为空
- 两个文件 合并为第三个文件 。
- 好物推薦:移動端開發安全工具
- 手机股票注册开户有没有什么风险?安全吗?
- 基于QT开发的线性代数初学者的矩阵计算器设计
- C primer plus学习笔记 —— 3、字符的IO(输入/输出)
猜你喜欢

Guomingyu: Apple's AR / MR head mounted display is the most complicated product in its history and will be released in January 2023

Dynamic planning 111

【贝叶斯分类3】半朴素贝叶斯分类器

leetcode刷题:哈希表08 (四数之和)

【连载】说透运维监控系统01-监控系统概述

MySQL - subquery usage

Muke 11. User authentication and authorization of microservices

leetcode刷题:字符串03(剑指 Offer 05. 替换空格)

云计算技术的发展与芯片处理器的关系
Mongodb implements creating and deleting databases, creating and deleting tables (sets), and adding, deleting, modifying, and querying data
随机推荐
Dynamic parameter association using postman
How to install mysql8.0 database under Windows system? (Graphic tutorial)
C language file cursor fseek
开发者调查:Rust/PostgreSQL 最受喜爱,PHP 薪水偏低
Detailed explanation of stored procedures in MySQL
Keep alive cache component in Vue
案例描述:比赛分数管理系统,需要统计历届冠军所得比赛得分,并记录到文件中,其中系统有如下需求:- 打开系统有欢迎界面,并显示可选择的选项- 选项1:记录比赛得分- 选项2:查看往届
Serial port application program based on gd32
Gamefi active users, transaction volume, financing amount and new projects continue to decline. Can axie and stepn get rid of the death spiral? Where is the chain tour?
好物推薦:移動端開發安全工具
【最详细】最新最全Redis面试大全(70道)
leetcode刷题:字符串03(剑指 Offer 05. 替换空格)
好物推荐:移动端开发安全工具
MySQL - table creation and management
StringUtils判断字符串是否为空
GEE:计算image区域内像素最大最小值
Swagger: how to generate beautiful static document description pages
C# 练习。类列表加记录,显示记录和清空记录
后台查找,如何查找网站后台
Is it safe to open an online account in case of five-year exemption?