国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

數(shù)據(jù)序列化工具Protobuf編碼&避坑指南

這篇具有很好參考價(jià)值的文章主要介紹了數(shù)據(jù)序列化工具Protobuf編碼&避坑指南。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

數(shù)據(jù)序列化工具Protobuf編碼&避坑指南

我們現(xiàn)在所有的協(xié)議、配置、數(shù)據(jù)庫(kù)的表達(dá)都是以 protobuf 來(lái)進(jìn)行承載的,所以我想深入總結(jié)一下 protobuf 這個(gè)協(xié)議,以免踩坑。

先簡(jiǎn)單介紹一下 Protocol Buffers(protobuf),它是 Google 開(kāi)發(fā)的一種數(shù)據(jù)序列化協(xié)議(與 XML、JSON 類似)。它具有很多優(yōu)點(diǎn),但也有一些需要注意的缺點(diǎn):

優(yōu)點(diǎn):

  1. 效率高:Protobuf 以二進(jìn)制格式存儲(chǔ)數(shù)據(jù),比如 XML 和 JSON 等文本格式更緊湊,也更快。序列化和反序列化的速度也很快。
  2. 跨語(yǔ)言支持:Protobuf 支持多種編程語(yǔ)言,包括 C++、Java、Python 等。
  3. 清晰的結(jié)構(gòu)定義:使用 protobuf,可以清晰地定義數(shù)據(jù)的結(jié)構(gòu),這有助于維護(hù)和理解。
  4. 向后兼容性:你可以添加或者刪除字段,而不會(huì)破壞老的應(yīng)用程序。這對(duì)于長(zhǎng)期的項(xiàng)目來(lái)說(shuō)是非常有價(jià)值的。

缺點(diǎn):

  1. 不直觀:由于 protobuf 是二進(jìn)制格式,人不能直接閱讀和修改它。這對(duì)于調(diào)試和測(cè)試來(lái)說(shuō)可能會(huì)有些困難。
  2. 缺乏一些數(shù)據(jù)類型:例如沒(méi)有內(nèi)建的日期、時(shí)間類型,對(duì)于這些類型的數(shù)據(jù),需要手動(dòng)轉(zhuǎn)換成可以支持的類型,如 string 或 int。
  3. 需要額外的編譯步驟:你需要先定義數(shù)據(jù)結(jié)構(gòu),然后使用 protobuf 的編譯器將其編譯成目標(biāo)語(yǔ)言的代碼,這是一個(gè)額外的步驟,可能會(huì)影響開(kāi)發(fā)流程。

總的來(lái)說(shuō),Protobuf 是一個(gè)強(qiáng)大而高效的數(shù)據(jù)序列化工具,我們一方面看重它的性能以及兼容性,除此之外就是它強(qiáng)制要求清晰的定義出來(lái),以文件的形式呈現(xiàn)出來(lái)方便我們維護(hù)管理。下面我們主要看它的編碼原理,以及在使用上有什么需要注意的地方。

?

編碼原理

概述

對(duì)于 protobuf 它的編碼是很緊湊的,我們先看一下 message 的結(jié)構(gòu),舉一個(gè)簡(jiǎn)單的例子:

message Student {
  string name = 1;
  int32 age = 2;
}

message 是一系列鍵值對(duì),編碼過(guò)之后實(shí)際上只有 tag 序列號(hào)和對(duì)應(yīng)的值,這一點(diǎn)相比我們熟悉的 json 很不一樣,所以對(duì)于 protobuf 來(lái)說(shuō)沒(méi)有?.proto?文件是無(wú)法解出來(lái)的:

數(shù)據(jù)序列化工具Protobuf編碼&避坑指南

對(duì)于 tag 來(lái)說(shuō),它保存了 message 字段的編號(hào)以及類型信息,我們可以做個(gè)實(shí)驗(yàn),把 name 這個(gè) tag 編碼后的二進(jìn)制打印出來(lái):

func?main()?{
?student?:=?student.Student{}
?student.Name?=?"t"
?marshal,?_?:=?proto.Marshal(&student)
?fmt.Println(fmt.Sprintf("%08b",?marshal))?//?00001010?00000001?01110100
}

打印出來(lái)的結(jié)果是這樣:

數(shù)據(jù)序列化工具Protobuf編碼&避坑指南

上圖中,由于 name 是 string 類型,所以第一個(gè) byte 是 tag,第二 byte 是 string 的長(zhǎng)度,第三個(gè) byte 是值,也就是我們上面設(shè)置的 “t”。我們下面先看看 tag:

數(shù)據(jù)序列化工具Protobuf編碼&避坑指南

tag 里面會(huì)包含兩部分信息:字段序號(hào),字段類型,計(jì)算方式就是上圖的公式。上圖中將 name 這個(gè)字段序列化成二進(jìn)制我們可以看到,第一個(gè) bit 是標(biāo)記位,表示是否字段結(jié)尾,這里是 0 表示 tag 已結(jié)尾,tag 占用 1byte;接下來(lái) 4 個(gè) bit 表示的是字段序號(hào),所以范圍 1 到 15 中的字段編號(hào)只需要 1 bit 進(jìn)行編碼,我們可以做個(gè)實(shí)驗(yàn)看看,將 tag 改成 16:

數(shù)據(jù)序列化工具Protobuf編碼&避坑指南
pb4

由上圖所示,每個(gè) byte 第一個(gè) bit 表示是否結(jié)束,0 表示結(jié)束,所以上面 tag 用兩個(gè) byte 表示,并且 protobuf 是小端編碼的,需要轉(zhuǎn)成大端方便閱讀,所以我們可以知道 tag 去掉每個(gè) byte 第一個(gè) bit 之后,后三位表示類型,是 3,其余位是編號(hào)表示 16。

所以從上面編碼規(guī)則我們也可以知道,字段盡可能精簡(jiǎn)一些,字段盡量不要超過(guò) 16 個(gè),這樣就可以用一個(gè) byte 表示了。

同時(shí)我們也可以知道,protobuf 序列化是不帶字段名的,所以如果客戶端的 proto 文件只修改了字段名,請(qǐng)求服務(wù)端是安全的,服務(wù)端繼續(xù)用根據(jù)序列編號(hào)還是解出來(lái)原來(lái)的字段。但是需要注意的是不要修改字段類型。

接下來(lái)我們看看類型,protobuf 共定義了 6 種類型,其中兩種是廢棄的:

ID Name Used For
0 VARINT int32, int64, uint32, uint64, sint32, sint64, bool, enum
1 I64 fixed64, sfixed64, double
2 LEN string, bytes, embedded messages, packed repeated fields
3 SGROUP group start (deprecated)
4 EGROUP group end (deprecated)
5 I32 fixed32, sfixed32, float

上面的例子中,Name 是 string 類型所以上面 tag 類型解出來(lái)是 010 ,也就是 2。

Varints 編碼

對(duì)于 protobuf 來(lái)說(shuō)對(duì)數(shù)字類型做了壓縮的,普通情況下一個(gè) int32 類型需要 4 byte,而 protobuf 表示 127 以內(nèi)的數(shù)字只需要 2 byte。因?yàn)閷?duì)于一個(gè)普通 int32 類型數(shù)字,如果數(shù)字很小,那么實(shí)際上有效位很少,比如要表示 1 這個(gè)數(shù)字,二進(jìn)制可能是這樣:

00000000?00000000?00000000?00000001

前 3 個(gè)字節(jié)都是 0 沒(méi)有表示任何信息,protobuf 就是將這些 0 都去除了,用 1 byte 表示 1 這個(gè)數(shù)字,再用 1 byte 表示 tag 的編號(hào)和類型,所以占用了 2byte。

比如我們對(duì)上面 student 設(shè)置 age 等于 150:

func?main()?{
?student?:=?student.Student{}
?student.Age?=?150
?marshal,?_?:=?proto.Marshal(&student)
?fmt.Println(fmt.Sprintf("%08b",?marshal))?//00010000?10010110?00000001
?fmt.Println(fmt.Sprintf("%08b",?"a"))
}

上面打印出來(lái)的二進(jìn)制如下,因?yàn)?150 超過(guò) 127,所以需要用兩個(gè) byte 表示:

數(shù)據(jù)序列化工具Protobuf編碼&避坑指南

第一個(gè) byte 是 tag 這里就不再重復(fù)介紹了。后面兩個(gè) byte 是真實(shí)的值,每個(gè) byte 的最高位 bit 是標(biāo)記位,表示是否結(jié)束。然后我們轉(zhuǎn)換成大端表示,串聯(lián)起來(lái)就可以得到它的值是 150。

ZigZag 編碼

Varints 編碼之所以可縮短數(shù)字所占的存儲(chǔ)字節(jié)數(shù)是因?yàn)槿サ袅?0 ,但是對(duì)于負(fù)數(shù)來(lái)說(shuō)就不行了,因?yàn)樨?fù)數(shù)的符號(hào)位為 1,并且對(duì)于 32 位的有符號(hào)數(shù)都會(huì)轉(zhuǎn)換成 64 位無(wú)符號(hào)來(lái)處理,例如 -1,用 Varints 編碼之后的二進(jìn)制:

數(shù)據(jù)序列化工具Protobuf編碼&避坑指南

所以 Varints 編碼負(fù)數(shù)總共會(huì)恒定占用 11 byte,tag 一個(gè) byte,值占用 10 byte。

為此 Google Protocol Buffer 定義了 sint32 這種類型,采用 zigzag 編碼。將所有整數(shù)映射成無(wú)符號(hào)整數(shù),然后再采用 varint 編碼方式編碼。例如:

Signed Original Encoded As
0 0
-1 1
1 2
-2 3
0x7fffffff 0xfffffffe
-0x80000000 0xffffffff

參照上面的表,也就是將 -1 編碼成 1,將 1 編碼成 2,全部都做映射,實(shí)際的 Zigzag 映射函數(shù)為:

(n?<<?1)?^?(n?>>?31)??//for?32?bit
(n?<<?1)?^?(n?>>?63)??//for?64?bit

對(duì)于使用來(lái)說(shuō),只是編碼方式變了,使用是不受影響,所以對(duì)于如果有很高比例負(fù)數(shù)的數(shù)據(jù),可以嘗試使用 sint 類型,節(jié)省一些空間。

embedded messages & repeated

比如現(xiàn)在定義這樣的 proto:

message Lecture {
  int32 price =1 ;
}

message Student {
  repeated int32 scores = 1;
  Lecture lecture = 2;
}

給 scores 取值為?[1,2,3],編碼之后發(fā)現(xiàn)其實(shí)和上面講的 string 類型很像。第一個(gè) byte 是 tag;第二 byte 是 len,長(zhǎng)度為 3;后面三個(gè) byte 都是值,我們?cè)O(shè)定的 1,2,3。

數(shù)據(jù)序列化工具Protobuf編碼&避坑指南

再來(lái)看看 embedded messages 類型,讓 Lecture 的 price 設(shè)置為 150 好了,編碼之后是這樣:

數(shù)據(jù)序列化工具Protobuf編碼&避坑指南

其實(shí)結(jié)構(gòu)也很簡(jiǎn)單,左邊的是 Student 類型,右邊是 Lecture 類型。有點(diǎn)不同的是對(duì)于 embedded messages 會(huì)將大小計(jì)算出來(lái)。

?

最佳實(shí)踐

字段編號(hào)

需要注意的是范圍 1 到 15 中的字段編號(hào)需要一個(gè)字節(jié)進(jìn)行編碼,包括字段編號(hào)和字段類型;范圍 16 至 2047 中的字段編號(hào)需要兩個(gè)字節(jié)。所以你應(yīng)該保留數(shù)字 1 到 15 作為非常頻繁出現(xiàn)的消息元素。

因?yàn)槭褂昧?VarInts,所以單字節(jié)的最高位是零,而最低三位表示類型,所以只剩下 4 位可用了。也就是說(shuō),當(dāng)你的字段數(shù)量超過(guò)?16?時(shí),就需要用兩個(gè)以上的字節(jié)表示了。

保留字段

一般的情況下,我們是不會(huì)輕易的刪除字段的,防止客戶端和服務(wù)端出現(xiàn)協(xié)議不一致的情況,如果您通過(guò)完全刪除某個(gè)字段或?qū)⑵渥⑨尩魜?lái)更新消息類型,那么未來(lái)的其他人不知道這個(gè) tag 或字段被刪除過(guò)了,我們可以使用 reserved 來(lái)標(biāo)記被刪除的字段,如:

message?Foo?{
??reserved?2,?15,?9?to?11;
??reserved?"foo",?"bar";
}

當(dāng)然除了 message ,reserved 也可以在枚舉類型中使用。

不要修改字段 tag 編號(hào)以及字段類型

protobuf 序列化是不帶字段名的,所以如果客戶端的 proto 文件只修改了字段名,請(qǐng)求服務(wù)端是安全的,服務(wù)端繼續(xù)用根據(jù)序列編號(hào)還是解出來(lái)原來(lái)的字段,但是需要注意的是不要修改字段類型,以及序列編號(hào),修改了之后就可能按照編號(hào)找錯(cuò)類型。

不要使用 required 關(guān)鍵字

required 意味著消息中必須包含這個(gè)字段,并且字段的值必須被設(shè)置。如果在序列化或者反序列化的過(guò)程中,該字段沒(méi)有被設(shè)置,那么 protobuf 庫(kù)就會(huì)拋出一個(gè)錯(cuò)誤。

如果你在初期定義了一個(gè) required 字段,但是在后來(lái)的版本中你想要?jiǎng)h除它,那么這就會(huì)造成問(wèn)題,因?yàn)榕f的代碼會(huì)期待該字段始終存在。為了確保兼容性,Google 在最新版本的 protobuf(protobuf 3)中已經(jīng)不再支持 required 修飾符。

盡量使用小整數(shù)

Varints 編碼表示 127 以內(nèi)的數(shù)字只需要 2 byte,1 byte 是 tag,1 byte 是值,壓縮效果很好。但是如果表示一個(gè)很大的數(shù)如 :1<<31 - 1,除去 tag 外需要占用 5 byte,比普通的 int 32 多 1 byte,因?yàn)?protobuf 每個(gè) byte 最高位有一個(gè)標(biāo)識(shí)符占用 1 bit。

如果需要傳輸負(fù)數(shù),可以試試 sint32 或 sint64

因?yàn)樨?fù)數(shù)的符號(hào)位為 1,并且 Varints 編碼對(duì)于負(fù)數(shù)如果是 32 位的有符號(hào)數(shù)都會(huì)轉(zhuǎn)換成 64 位無(wú)符號(hào)來(lái)處理,所以 Varints 編碼負(fù)數(shù)總共會(huì)恒定占用 11 byte,tag 一個(gè) byte,值占用 10 byte。

而 sint32 和 sint64 將所有整數(shù)映射成無(wú)符號(hào)整數(shù),然后再采用 varint 編碼方式編碼,如果數(shù)字比較還是可以節(jié)省一定的空間的。

Reference

https://sunyunqiang.com/blog/protobuf_encode/

https://halfrost.com/protobuf_encode/

https://protobuf.dev/programming-guides/encoding/

https://protobuf.dev/programming-guides/dos-donts/

?

作者:bear文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-645060.html

到了這里,關(guān)于數(shù)據(jù)序列化工具Protobuf編碼&避坑指南的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • Unity基于Google Protobuf序列化和反序列化小案例

    Unity基于Google Protobuf序列化和反序列化小案例

    1.協(xié)議定義,簡(jiǎn)單實(shí)現(xiàn)傳玩家的2D坐標(biāo)? ? 2.在Unity的Assets目錄下創(chuàng)建一個(gè)Plugins文件夾(必須這樣命名),此文件專門(mén)存放擴(kuò)展文件, 再新建文件夾BaseInfolibrary,將Google.Protobuf.dll拖入 ?3.新建一個(gè)Test.cs腳本 ?腳本中引入命名空間 代碼改進(jìn):通用序列化模板(只用來(lái)序列化Message)

    2024年02月15日
    瀏覽(28)
  • 在Unity中使用Protobuf進(jìn)行序列化

    在Unity中使用Protobuf進(jìn)行序列化

    目錄 1.介紹 1.1 什么是Protobuf 1.2 Protobuf和其他數(shù)據(jù)序列化方案對(duì)比 2.下載Protobuf 2.1?方案一 使用VS的Nuget包管理器進(jìn)行安裝(推薦) 2.1.1安裝Protobuff包 2.1.2拷貝.dll文件 2.2 方案二 從Github下載并自己生成.dll 2.2.1 下載Probuff 2.2.2?VS打開(kāi)解決方案 2.2.3 安裝.NET SDK 2.2.4 生成.dll文件 3

    2024年04月12日
    瀏覽(30)
  • 【RPC 協(xié)議】序列化與反序列化 | lua-cjson | lua-protobuf

    【RPC 協(xié)議】序列化與反序列化 | lua-cjson | lua-protobuf

    在分布式計(jì)算,遠(yuǎn)程過(guò)程調(diào)用(英語(yǔ):Remote Procedure Call,縮寫(xiě)為 RPC)是一個(gè)計(jì)算機(jī)通信協(xié)議。該協(xié)議允許運(yùn)行于一臺(tái)計(jì)算機(jī)的程序調(diào)用另一個(gè)地址空間(通常為一個(gè)開(kāi)放網(wǎng)絡(luò)的一臺(tái)計(jì)算機(jī))的子程序,而程序員就像調(diào)用本地程序一樣,無(wú)需額外地為這個(gè)交互作用編程(無(wú)需關(guān)

    2024年02月10日
    瀏覽(22)
  • [Linux] 初識(shí)應(yīng)用層協(xié)議: 序列化與反序列化、編碼與解碼、jsoncpp簡(jiǎn)單使用...

    [Linux] 初識(shí)應(yīng)用層協(xié)議: 序列化與反序列化、編碼與解碼、jsoncpp簡(jiǎn)單使用...

    有關(guān)Linux網(wǎng)絡(luò), 之前的文章已經(jīng)簡(jiǎn)單演示介紹了 UDP 、 TCP 套接字編程 相關(guān)文章: [Linux] 網(wǎng)絡(luò)編程 - 初見(jiàn)UDP套接字編程: 網(wǎng)絡(luò)編程部分相關(guān)概念、TCP、UDP協(xié)議基本特點(diǎn)、網(wǎng)絡(luò)字節(jié)序、socket接口使用、簡(jiǎn)單的UDP網(wǎng)絡(luò)及聊天室實(shí)現(xiàn)… [Linux] 網(wǎng)絡(luò)編程 - 初見(jiàn)TCP套接字編程: 實(shí)現(xiàn)簡(jiǎn)單的單

    2024年02月15日
    瀏覽(28)
  • .NET的8種JSON序列化反序列化工具供你選擇

    在.NET開(kāi)發(fā)中,.NET的JSON序列化反序列化工具除了Newtonsoft.Json和System.Text.Json其實(shí)還有很多優(yōu)秀的開(kāi)源的序列化和反序列化工具,這些工具有的性能更加優(yōu)秀,更加輕量等特征。本文將匯總介紹這些.NET中常用的JSON序列化和反序列化工具,供大家選擇參考使用。 1、Newtonsoft.Json

    2024年02月08日
    瀏覽(26)
  • SharedPreferences工具類保存List對(duì)象,自動(dòng)完成序列化和反序列化

    以下是一個(gè)示例的SharedPreferences工具類,其中包含了setList()和getList()方法,用于將List序列化為JSON字符串并存儲(chǔ)到SharedPreferences中,以及從SharedPreferences中獲取JSON字符串并反序列化為L(zhǎng)ist對(duì)象: 在上述代碼中,我們定義了一個(gè)SharedPreferencesUtils工具類,其中包含了setList()和getLis

    2024年02月16日
    瀏覽(22)
  • shiro反序列化漏洞學(xué)習(xí)(工具+原理+復(fù)現(xiàn))

    shiro反序列化漏洞學(xué)習(xí)(工具+原理+復(fù)現(xiàn))

    工具準(zhǔn)備 1.java8 C:Program FilesJava 2.冰蝎 C:UsersaliDesktoptoolsBehinder_v4.0.6 3.shiro反序列化 圖形化工具 shiro attack2.2 C:UsersaliDesktoptoolsshiro_attack_2.2 4.shiro反序列化 命令行工具 C:UsersaliDesktoptoolsshiro_tool.jar 一.Shiro-550 CVE-2016-4437 序列化: 在Java中,把Java對(duì)象轉(zhuǎn)換為字節(jié)序列(

    2023年04月08日
    瀏覽(23)
  • 后端項(xiàng)目開(kāi)發(fā):工具類封裝(序列化、反射)

    根據(jù)《阿里巴巴開(kāi)發(fā)規(guī)范》,包名使用單數(shù),類名可以使用復(fù)數(shù)。 所以generic-common創(chuàng)建util包和utils工具類 很多時(shí)候我們需要將接收到的json數(shù)據(jù)轉(zhuǎn)換為對(duì)象,或者將對(duì)象轉(zhuǎn)為json存儲(chǔ)。這時(shí)候我們需要編寫(xiě)用于json轉(zhuǎn)換的工具類。 新建util目錄,再創(chuàng)建JacksonUtils類

    2024年02月11日
    瀏覽(21)
  • 【工具篇】SerializableDictionary字典序列化Unity面板顯示

    目錄 一:導(dǎo)入插件 二:創(chuàng)建目標(biāo)字典類 三:生成數(shù)據(jù) ?四:自定義配置數(shù)據(jù) Unity本身對(duì)字典這種數(shù)據(jù)結(jié)構(gòu)沒(méi)有序列

    2024年02月15日
    瀏覽(42)
  • WebLogic反序列化漏洞復(fù)現(xiàn)+利用工具(CVE-2021-2394)

    WebLogic反序列化漏洞復(fù)現(xiàn)+利用工具(CVE-2021-2394)

    Oracle官方發(fā)布了2021年7月份安全更新通告,通告中披露了WebLogic組件存在高危漏洞,攻擊者可以在未授權(quán)的情況下通過(guò)IIOP、T3協(xié)議對(duì)存在漏洞的WebLogic Server組件進(jìn)行攻擊。成功利用該漏洞的攻擊者可以接管WebLogic Server。 這是一個(gè)二次反序列化漏洞,是CVE-2020-14756和CVE-2020-14825的

    2024年02月06日
    瀏覽(37)

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包