外國(guó)優(yōu)秀網(wǎng)站設(shè)計(jì)青島關(guān)鍵詞優(yōu)化平臺(tái)
Protobuf 3 入門(mén)
1. 什么是序列化?
1.1 概念
序列化(Serialization 或 Marshalling) 是指將數(shù)據(jù)結(jié)構(gòu)或?qū)ο蟮臓顟B(tài)轉(zhuǎn)換成可存儲(chǔ)或傳輸?shù)母袷?/strong>。反向操作稱(chēng)為反序列化(Deserialization 或 Unmarshalling),它的作用是將序列化的數(shù)據(jù)恢復(fù)成原始的數(shù)據(jù)結(jié)構(gòu)或?qū)ο蟆?/p>
簡(jiǎn)單來(lái)說(shuō),序列化就像“打包”,反序列化就像“解包”。 在計(jì)算機(jī)系統(tǒng)中,數(shù)據(jù)通常是以內(nèi)存中的對(duì)象(如 不同的序列化格式適用于不同的應(yīng)用場(chǎng)景,常見(jiàn)的格式包括: Protobuf(Protocol Buffers)是 Google 開(kāi)發(fā)的一種高效、跨平臺(tái)、可擴(kuò)展的數(shù)據(jù)序列化協(xié)議。它可以將數(shù)據(jù)轉(zhuǎn)換為緊湊的二進(jìn)制格式,用于不同系統(tǒng)之間進(jìn)行高效的數(shù)據(jù)傳輸和存儲(chǔ)。 簡(jiǎn)單理解: 如果你的項(xiàng)目涉及: 那么 Protobuf 是比 JSON、XML 更好的選擇。 解釋 Protobuf 需要編譯后才能用于編程語(yǔ)言(Go、Java、Python 等)。 在終端運(yùn)行: 不同語(yǔ)言對(duì)應(yīng)的參數(shù): 最終會(huì)生成 編譯后會(huì)生成如下 Go 結(jié)構(gòu): 解釋 解釋 Protobuf 提供了一些常見(jiàn)的基本數(shù)據(jù)類(lèi)型,對(duì)應(yīng)不同語(yǔ)言的變量類(lèi)型。 區(qū)別: 注意: 示例: 示例: 注意: 使用 注意: Protobuf 3 提供 這個(gè) 在 適用場(chǎng)景 Protobuf 生成的 示例:Go 代碼 輸出 在 Protobuf 3 中, 當(dāng)你刪除或修改字段時(shí),如果不使用 你可以用 保留字段編號(hào) 保留字段名稱(chēng) 同時(shí)保留編號(hào)和名稱(chēng) (1)導(dǎo)入 (2)嵌套不同的消息 假設(shè)你有兩種不同的消息 (1)安裝 Protobuf 依賴(lài) 在 Go 代碼中,需要 (2)Go 代碼示例 (3)Go 代碼解釋 Protobuf 使用高效的二進(jìn)制格式來(lái)存儲(chǔ)和傳輸數(shù)據(jù),其中最關(guān)鍵的編碼方式之一是 Varint(變長(zhǎng)整數(shù)編碼)。它的核心思想是: Varint(變長(zhǎng)整數(shù)編碼) 是一種特殊的編碼方式,它可以使用 1 到 N 個(gè)字節(jié) 表示整數(shù)。 (1)數(shù)字 存儲(chǔ)方式: (2)數(shù)字 先看二進(jìn)制表示: 需要拆成 7 位 + 剩余部分: 最終編碼 Protobuf 數(shù)據(jù)存儲(chǔ)為 鍵值對(duì)(key-value) 形式,每個(gè)字段的 存儲(chǔ)格式 字段編號(hào)左移 3 位,低 3 位存 Wire Type。 假設(shè) 數(shù)據(jù): 編碼過(guò)程 字段 最終存儲(chǔ): 字段 最終存儲(chǔ): 假設(shè)收到如下二進(jìn)制數(shù)據(jù): 逐字節(jié)解析: 最終解析為:2.1 為什么需要序列化?
struct
、class
)形式存在的,而內(nèi)存數(shù)據(jù)不能直接在不同程序之間傳輸,必須先轉(zhuǎn)換成可存儲(chǔ)或可傳輸?shù)母袷?。序列化的主要用途包?#xff1a;3.1 序列化的方式
格式 特點(diǎn) 可讀性 序列化速度 數(shù)據(jù)大小 適用場(chǎng)景 JSON 文本格式,廣泛使用 可讀 適中 較大 Web API,前端后端通信 XML 結(jié)構(gòu)化文本,標(biāo)簽冗余 可讀 慢 大 早期 Web API,配置文件 YAML 結(jié)構(gòu)更簡(jiǎn)潔,適合人閱讀 可讀 適中 較大 配置文件(Kubernetes、Docker) Protobuf Google 開(kāi)發(fā)的高效二進(jìn)制格式 不可讀 快 小 微服務(wù)、gRPC、高性能應(yīng)用 MessagePack 類(lèi)似 JSON,但體積更小 不可讀 快 小 移動(dòng)端、嵌入式系統(tǒng) Thrift Facebook 開(kāi)發(fā)的高效序列化格式 不可讀 快 小 分布式系統(tǒng),RPC Avro 適用于大數(shù)據(jù)(如 Hadoop) 不可讀 適中 小 大數(shù)據(jù)處理 BSON MongoDB 的序列化格式 不可讀 適中 適中 MongoDB 數(shù)據(jù)存儲(chǔ) 2. 什么是 Protobuf?
2.1 概念
2.2 為什么使用 Protobuf?
特點(diǎn) Protobuf JSON XML 格式 二進(jìn)制 文本 文本 體積 最小 較大 最大 解析速度 最快 一般 最慢 可讀性 不可讀 可讀 可讀 跨語(yǔ)言支持 是 是 是 支持 RPC 是(gRPC) 否 否 2.3 Protobuf 的使用場(chǎng)景
gRPC
使用,可以比傳統(tǒng) REST API
更快。3. 簡(jiǎn)單解釋 Protobuf 例子
3.1 Protobuf 文件
simple.proto
syntax = "proto3"; // 使用 proto3 語(yǔ)法message SearchRequest { // 定義一個(gè)數(shù)據(jù)結(jié)構(gòu)(類(lèi)似 JSON 對(duì)象)string query = 1; // 搜索關(guān)鍵詞(字符串)int32 page_number = 2; // 頁(yè)碼(整數(shù))int32 result_per_page = 3; // 每頁(yè)返回的結(jié)果數(shù)(整數(shù))
}
syntax = "proto3";
指定使用 proto3
語(yǔ)法。message SearchRequest
定義了一個(gè)數(shù)據(jù)結(jié)構(gòu)(類(lèi)似 JSON 對(duì)象)。string
、int32
)query
、page_number
、result_per_page
)1
、2
、3
,用于唯一標(biāo)識(shí)字段,不能重復(fù))3.2 編譯 Protobuf 代碼
protoc --go_out=. simple.proto
protoc
是 Protobuf 編譯器--go_out=.
表示生成 Go 代碼,并存放在當(dāng)前目錄simple.proto
是需要編譯的 Protobuf 文件語(yǔ)言 編譯參數(shù) C++ --cpp_out=.
Java --java_out=.
Python --python_out=.
C# --csharp_out=.
Rust --rust_out=.
simple.pb.go
,這個(gè)文件包含 Go 代碼,用于操作 SearchRequest
結(jié)構(gòu)。3.3 生成的 Go 代碼
type SearchRequest struct {Query string `protobuf:"bytes,1,opt,name=query,proto3" json:"query,omitempty"`PageNumber int32 `protobuf:"varint,2,opt,name=page_number,json=pageNumber,proto3" json:"page_number,omitempty"`ResultPerPage int32 `protobuf:"varint,3,opt,name=result_per_page,json=resultPerPage,proto3" json:"result_per_page,omitempty"`
}
SearchRequest
是 struct
,對(duì)應(yīng) .proto
文件中的 message SearchRequest
。Query
、PageNumber
、ResultPerPage
變量對(duì)應(yīng) .proto
里的字段。protobuf:"..."
里的信息用于 Protobuf 序列化和解析。3.4 如何使用這個(gè) Go 結(jié)構(gòu)
package mainimport ("fmt""google.golang.org/protobuf/proto"
)func main() {// 創(chuàng)建 SearchRequest 實(shí)例request := &SearchRequest{Query: "golang protobuf",PageNumber: 1,ResultPerPage: 10,}// **序列化**data, _ := proto.Marshal(request)// **反序列化**newRequest := &SearchRequest{}proto.Unmarshal(data, newRequest)fmt.Println(newRequest) // 輸出: {Query:golang protobuf PageNumber:1 ResultPerPage:10}
}
SearchRequest
結(jié)構(gòu),并填充數(shù)據(jù)。proto.Marshal(request)
序列化,轉(zhuǎn)換成二進(jìn)制格式(適合網(wǎng)絡(luò)傳輸)。proto.Unmarshal(data, newRequest)
反序列化,把二進(jìn)制恢復(fù)成 Go 結(jié)構(gòu)。4. Protobuf 的數(shù)據(jù)類(lèi)型
4.1 標(biāo)量數(shù)據(jù)類(lèi)型(Scalar Types)
4.1.1 數(shù)值類(lèi)型
Protobuf 類(lèi)型 說(shuō)明 適用場(chǎng)景 int32
32 位整數(shù)(默認(rèn)編碼) 適用于較小的整數(shù) int64
64 位整數(shù)(默認(rèn)編碼) 適用于較大的整數(shù) uint32
無(wú)符號(hào) 32 位整數(shù) 適用于只能為正數(shù)的情況 uint64
無(wú)符號(hào) 64 位整數(shù) 適用于大數(shù)且不允許負(fù)數(shù) sint32
32 位有符號(hào)整數(shù)(ZigZag 編碼) 適用于可能包含負(fù)數(shù)的整數(shù) sint64
64 位有符號(hào)整數(shù)(ZigZag 編碼) 適用于包含負(fù)數(shù)的長(zhǎng)整數(shù) fixed32
32 位整數(shù)(固定長(zhǎng)度編碼) 適用于數(shù)值分布較均勻的場(chǎng)景 fixed64
64 位整數(shù)(固定長(zhǎng)度編碼) 適用于較大的定長(zhǎng)整數(shù) sfixed32
32 位有符號(hào)整數(shù)(固定長(zhǎng)度編碼) 適用于負(fù)數(shù)較多的場(chǎng)景 sfixed64
64 位有符號(hào)整數(shù)(固定長(zhǎng)度編碼) 適用于較大的負(fù)數(shù)
int32/int64
:默認(rèn)使用 Varint 編碼(數(shù)據(jù)小的時(shí)候占用字節(jié)更少)。sint32/sint64
:使用 ZigZag 編碼,負(fù)數(shù)編碼更高效。fixed32/fixed64
:使用固定長(zhǎng)度存儲(chǔ),適合數(shù)值分布均勻的情況。sfixed32/sfixed64
:固定長(zhǎng)度的有符號(hào)整數(shù)。4.1.2 浮點(diǎn)數(shù)類(lèi)型
Protobuf 類(lèi)型 說(shuō)明 適用場(chǎng)景 float
32 位浮點(diǎn)數(shù) 適用于存儲(chǔ)小數(shù) double
64 位浮點(diǎn)數(shù) 適用于更高精度的小數(shù)
float
占 4 個(gè)字節(jié),精度有限。double
占 8 個(gè)字節(jié),適用于更高精度計(jì)算。4.1.3 布爾類(lèi)型
Protobuf 類(lèi)型 說(shuō)明 適用場(chǎng)景 bool
布爾值 ( true/false
)適用于開(kāi)關(guān)、狀態(tài)等 message Example {bool is_active = 1; // true or false
}
4.1.4 字符串和字節(jié)類(lèi)型
Protobuf 類(lèi)型 說(shuō)明 適用場(chǎng)景 string
UTF-8 編碼的字符串 存儲(chǔ)文本信息 bytes
原始字節(jié)數(shù)據(jù) 適用于存儲(chǔ)二進(jìn)制數(shù)據(jù)(如文件、圖片等) message Example {string name = 1;bytes file_data = 2;
}
string
只能存儲(chǔ) 文本(UTF-8 編碼)。bytes
可以存儲(chǔ) 任意二進(jìn)制數(shù)據(jù)(如圖片、視頻等)。4.2 復(fù)雜數(shù)據(jù)類(lèi)型
4.2.1 數(shù)組(Repeated)
repeated
關(guān)鍵字表示 列表/數(shù)組:message Example {repeated string hobbies = 1;repeated int32 scores = 2;
}
repeated string hobbies = 1;
→ 表示字符串?dāng)?shù)組repeated int32 scores = 2;
→ 表示整數(shù)數(shù)組
repeated
類(lèi)型默認(rèn)是可選的,不需要額外的 optional
關(guān)鍵字。4.2.2 鍵值對(duì)(Map)
map<K, V>
類(lèi)型來(lái)存儲(chǔ)鍵值對(duì):message Example {map<string, int32> scores = 1; // key: string, value: int32
}
map<string, int32>
表示 鍵為字符串,值為整數(shù) 的字典。map[string]int32
。4.2.3 枚舉類(lèi)型(Enum)
enum Status {UNKNOWN = 0; // 枚舉必須從 0 開(kāi)始ACTIVE = 1;INACTIVE = 2;
}message User {Status status = 1;
}
enum
只能用于定義固定的值(類(lèi)似 int
)。4.2.4 嵌套 Message
message Address {string city = 1;string street = 2;
}message Person {string name = 1;Address address = 2; // 直接嵌套 Address
}
Person
結(jié)構(gòu)里包含 Address
結(jié)構(gòu),可以用于復(fù)雜數(shù)據(jù)存儲(chǔ)。4.3 Protobuf 類(lèi)型與不同語(yǔ)言的對(duì)應(yīng)關(guān)系
Protobuf 類(lèi)型 Go Java Python C++ int32
int32
int
int
int32_t
int64
int64
long
int
int64_t
float
float32
float
float
float
double
float64
double
float
double
bool
bool
boolean
bool
bool
string
string
String
str
std::string
bytes
[]byte
byte[]
bytes
std::string
map<K,V>
map[K]V
Map<K,V>
dict
std::map<K,V>
repeated
[]T
List<T>
list
std::vector<T>
4.4 Protobuf 3 語(yǔ)法示例
syntax = "proto3";message Person {string name = 1;int32 age = 2;bool is_active = 3;repeated string hobbies = 4;map<string, int32> scores = 5;
}
Person
結(jié)構(gòu)包含:string name
→ 姓名int32 age
→ 年齡bool is_active
→ 是否激活repeated string hobbies
→ 興趣愛(ài)好(數(shù)組)map<string, int32> scores
→ 課程成績(jī)(鍵值對(duì))5. Protobuf 其他字段
5.1 Oneof(互斥字段)
5.1.1 什么是
oneof
?oneof
關(guān)鍵字用于定義一組互斥字段,即同一時(shí)間只能有一個(gè)字段被設(shè)置。它的作用類(lèi)似于 C 語(yǔ)言的 union
,但比 union
更智能,可以判斷當(dāng)前設(shè)置的是哪個(gè)字段。5.1.2 為什么要用
oneof
?proto3
版本中,所有字段都有默認(rèn)值,比如:message Example {int64 id = 1;
}
id
沒(méi)有被設(shè)置,默認(rèn)值是 0
。id
被顯式設(shè)置為 0
,你就無(wú)法判斷這個(gè) 0
是默認(rèn)值,還是用戶(hù)真的設(shè)置了 0
。oneof
解決了這個(gè)問(wèn)題,因?yàn)樗峁┝艘粋€(gè)字段狀態(tài)檢查功能,讓你可以判斷哪個(gè)字段被設(shè)置了。5.1.3
oneof
語(yǔ)法message Response {oneof result {string success_message = 1; // 成功時(shí)的消息int32 error_code = 2; // 失敗時(shí)的錯(cuò)誤碼}
}
oneof
內(nèi)的字段是互斥的,最多只能設(shè)置一個(gè)。success_message
被設(shè)置,error_code
就不能被設(shè)置,反之亦然。oneof
字段為空。
success_message
,失敗返回 error_code
)。5.1.4
oneof
在 Go 語(yǔ)言中的使用Go
代碼會(huì)使用 isXxx()
方法 來(lái)判斷哪個(gè)字段被賦值。package mainimport ("fmt""google.golang.org/protobuf/proto"
)// 假設(shè) Protobuf 生成的 Go 結(jié)構(gòu)如下:
type Response struct {// 這是 oneof 生成的字段Result isResponse_Result `protobuf_oneof:"result"`
}type isResponse_Result interface {isResponse_Result()
}type Response_SuccessMessage struct {SuccessMessage string
}type Response_ErrorCode struct {ErrorCode int32
}// 實(shí)現(xiàn) isResponse_Result 接口
func (*Response_SuccessMessage) isResponse_Result() {}
func (*Response_ErrorCode) isResponse_Result() {}func main() {// **成功時(shí)返回 success_message**resp1 := &Response{Result: &Response_SuccessMessage{SuccessMessage: "Operation successful"}}// **失敗時(shí)返回 error_code**resp2 := &Response{Result: &Response_ErrorCode{ErrorCode: 404}}// 判斷是哪個(gè)字段被設(shè)置switch v := resp1.Result.(type) {case *Response_SuccessMessage:fmt.Println("Success:", v.SuccessMessage)case *Response_ErrorCode:fmt.Println("Error:", v.ErrorCode)}switch v := resp2.Result.(type) {case *Response_SuccessMessage:fmt.Println("Success:", v.SuccessMessage)case *Response_ErrorCode:fmt.Println("Error:", v.ErrorCode)}
}
Success: Operation successful
Error: 404
oneof
生成了 isResponse_Result
接口,允許我們判斷哪個(gè)字段被設(shè)置。switch v := resp.Result.(type)
語(yǔ)法用于檢查當(dāng)前 oneof
字段的類(lèi)型。5.1.5
oneof
的應(yīng)用場(chǎng)景未支付
/ 已支付
)郵箱登錄
/ 手機(jī)號(hào)登錄
)文本
/ 圖片
/ 視頻
)5.2 Reserved(保留字段)
5.2.1 什么是
reserved
?reserved
關(guān)鍵字用于保留字段編號(hào)或名稱(chēng),防止將來(lái)代碼演進(jìn)時(shí)誤用已刪除的字段。
這可以避免 API 變更時(shí)的兼容性問(wèn)題,確保舊數(shù)據(jù)不會(huì)被錯(cuò)誤解析。5.2.2 為什么需要
reserved
?reserved
,那么:5.2.3
reserved
語(yǔ)法reserved
關(guān)鍵字保留字段編號(hào)或名稱(chēng),防止后續(xù)被重新使用。message User {reserved 2, 4 to 6; // 不能再使用編號(hào) 2、4、5、6
}
field = 2;
,編譯時(shí)會(huì)報(bào)錯(cuò)。message User {reserved "old_name", "deprecated_field"; // 不能再使用這些字段名
}
message User {reserved 2, 4 to 6;reserved "old_name", "deprecated_field";
}
5.2.4
reserved
作用5.3 Any(存儲(chǔ)任意數(shù)據(jù))
5.3.1 什么是
Any
?Any
是 Protobuf 3 提供的一種特殊類(lèi)型,允許存儲(chǔ)任意類(lèi)型的 Protobuf 消息,適用于動(dòng)態(tài)數(shù)據(jù)場(chǎng)景。
它可以在不修改 .proto
結(jié)構(gòu)的情況下,支持不同類(lèi)型的數(shù)據(jù),類(lèi)似于 JSON 里的 object
或 map<string, any>
。5.3.2
Any
的作用User
或 Order
),可以使用 Any
而不需要改 .proto
文件。Any
可以減少 API 變更的影響。5.3.3
Any
的基本用法Any
類(lèi)型Any
需要導(dǎo)入 google/protobuf/any.proto
:import "google/protobuf/any.proto"; // 引入 Any 類(lèi)型message Response {string message = 1; google.protobuf.Any data = 2; // 存儲(chǔ)任意類(lèi)型
}
message
是普通字段,存儲(chǔ)文本信息。data
是 Any
類(lèi)型,可以存儲(chǔ)任何 Protobuf 消息。User
和 Order
:message User {string name = 1;int32 age = 2;
}message Order {int32 order_id = 1;double price = 2;
}message Response {string message = 1;google.protobuf.Any data = 2; // 可以存儲(chǔ) User 或 Order
}
data
字段可以存儲(chǔ) User
或 Order
,而不需要修改 Response
結(jié)構(gòu)。Response
可以在不同場(chǎng)景下使用,不受數(shù)據(jù)類(lèi)型影響。5.3.4
Any
在 Go 語(yǔ)言中的使用proto
和 anypb
(處理 Any
類(lèi)型):go get google.golang.org/protobuf/proto
go get google.golang.org/protobuf/types/known/anypb
package mainimport ("fmt""google.golang.org/protobuf/proto""google.golang.org/protobuf/types/known/anypb"
)// 定義 User 和 Order 結(jié)構(gòu)
type User struct {Name stringAge int32
}type Order struct {OrderId int32Price float64
}// Response 結(jié)構(gòu),包含 Any 字段
type Response struct {Message stringData *anypb.Any
}func main() {// 創(chuàng)建 User 結(jié)構(gòu)user := &User{Name: "Alice", Age: 25}// 將 User 結(jié)構(gòu)封裝到 Any 里anyData, _ := anypb.New(user)// 創(chuàng)建 Response 并存儲(chǔ) User 數(shù)據(jù)resp := &Response{Message: "User data",Data: anyData,}// **序列化**data, _ := proto.Marshal(resp)// **反序列化**newResp := &Response{}proto.Unmarshal(data, newResp)// **解析 Any 字段**newUser := &User{}newResp.Data.UnmarshalTo(newUser)fmt.Println("Message:", newResp.Message)fmt.Println("User Name:", newUser.Name, "Age:", newUser.Age)
}
anypb.New(user)
把 User
結(jié)構(gòu)轉(zhuǎn)換成 Any
類(lèi)型。Response
: proto.Marshal(resp)
進(jìn)行序列化,便于存儲(chǔ)或傳輸。Response
: proto.Unmarshal(data, newResp)
解析 Response
結(jié)構(gòu)。Any
數(shù)據(jù): newResp.Data.UnmarshalTo(newUser)
解析 Any
字段,恢復(fù) User
結(jié)構(gòu)。6. Protobuf 編碼原理
6.1 什么是 Varint?
1
只需要 1 個(gè)字節(jié))。300
需要 2 個(gè)字節(jié))。6.2 Varint 編碼規(guī)則
0
:表示這是最后一個(gè)字節(jié)。1
:表示后面還有字節(jié)。6.3 具體示例
1
的 Varint 編碼0000 0001 (只有 1 個(gè)字節(jié))
0
:表示這是最后一個(gè)字節(jié)。000 0001
(= 1)。[0000 0001] → 1 字節(jié)
300
的 Varint 編碼300 = 100101100(9 位)
低 7 位: 0101100 → 0x2C(44)
高 2 位: 0000010 → 0x02(2)
1010 1100
(0xAC) 1
(表示后面還有字節(jié))。7
位存 010 1100
(= 44)。0000 0010
(0x02) 0
(表示這是最后一個(gè)字節(jié))。7
位存 000 0010
(= 2)。[1010 1100] [0000 0010] → 2 字節(jié)(0xAC 0x02)
6.4 Wire Type(數(shù)據(jù)類(lèi)型編碼)
key
也需要編碼。
字段的 key
由字段編號(hào) + Wire Type 組成。Wire Type 值 作用 Varint
0
變長(zhǎng)整數(shù)( int32, int64, bool, enum
)Fixed64
1
64 位定長(zhǎng)( double, fixed64, sfixed64
)Length-delimited
2
變長(zhǎng)數(shù)據(jù)( string, bytes, message, repeated
)Start group
3
已廢棄(用于嵌套數(shù)據(jù)) End group
4
已廢棄 Fixed32
5
32 位定長(zhǎng)( float, fixed32, sfixed32
)[字段編號(hào) << 3] | [Wire Type] [數(shù)據(jù)]
6.5 例子:Protobuf 編碼解析
Person
結(jié)構(gòu)如下:message Person {int32 id = 1; // 1 字段編號(hào)string name = 2; // 2 字段編號(hào)
}
{"id": 150,"name": "Alice"
}
id = 150
1
0
(Varint)key = (1 << 3) | 0 = 0000 1000 (0x08)
150
的 Varint 編碼:1001 0110 0000 0001
(0x96 0x01)[0x08] [0x96 0x01] (字段編號(hào) 1,Varint)
name = "Alice"
2
2
(Length-delimited,字符串)key = (2 << 3) | 2 = 0001 0001 (0x12)
"Alice"
= 5 個(gè)字節(jié)(0x41 0x6C 0x69 0x63 0x65
)[0x12] [0x05] [0x41 0x6C 0x69 0x63 0x65]
6.6 解析 Protobuf 二進(jìn)制數(shù)據(jù)
08 96 01 12 05 41 6C 69 63 65
08
= 0000 1000
(字段編號(hào) 1
,Wire Type 0
,Varint)96 01
= 150(Varint 解碼)12
= 0001 0010
(字段編號(hào) 2
,Wire Type 2
,字符串)05
= 長(zhǎng)度 5
41 6C 69 63 65
= "Alice"
{"id": 150,"name": "Alice"
}