ProtoBuf 是google團隊開發的用于高效存儲和讀取結構化數據的工具。什么是結構化數據呢,正如字面上表達的,就是帶有一定結構的數據。比如電話簿上有很多記錄數據,每條記錄包含姓名、ID、郵件、電話等,這種結構重復出現。
同類
XML、JSON 也可以用來存儲此類結構化數據,但是使用ProtoBuf表示的數據能更加高效,并且將數據壓縮得更小。
原理
ProtoBuf 是通過ProtoBuf編譯器將與編程語言無關的特有的 .proto 后綴的數據結構文件編譯成各個編程語言(Java,C/C++,Python)專用的類文件,然后通過Google提供的各個編程語言的支持庫lib即可調用API。(關于proto結構體怎么編寫,可自行查閱文檔)
ProtoBuf編譯器安裝
Mac : brew install protobuf
舉個例子
1.先創建一個proto文件
message.proto
syntax?=?"proto3"; ? message?Person?{ ????int32?id?=?1; ????string?name?=?2; ???? ????repeated?Phone?phone?=?4; ???? ????enum?PhoneType?{ ????????MOBILE?=?0; ????????HOME?=?1; ????????WORK?=?2; ????} ? ????message?Phone?{ ????????string?number?=?1; ????????PhoneType?type?=?2; ????} }
?
?
2. 創建一個Java項目
并且將proto文件放置 src/main/proto 文件夾下
3. 編譯proto文件至Java版本
用命令行 cd 到 src/main 目錄下
終端執行命令 : protoc --java_out=./java ./proto/*.proto
會發現,在你的src/main/java 里已經生成里對應的Java類
4. 依賴Java版本的ProtoBuf支持庫
這里只舉一個用Gradle使用依賴的栗子
implementation?'com.google.protobuf3.9.1'
5. 將Java對象轉為ProtoBuf數據
Message.Person.Phone.Builder?phoneBuilder?=?Message.Person.Phone.newBuilder(); Message.Person.Phone?phone1?=?phoneBuilder ????????.setNumber("100860") ????????.setType(Message.Person.PhoneType.HOME) ????????.build(); Message.Person.Phone?phone2?=?phoneBuilder ????????.setNumber("100100") ????????.setType(Message.Person.PhoneType.MOBILE) ????????.build(); Message.Person.Builder?personBuilder?=?Message.Person.newBuilder(); personBuilder.setId(1994); personBuilder.setName("XIAOLEI"); personBuilder.addPhone(phone1); personBuilder.addPhone(phone2); Message.Person?person?=?personBuilder.build(); long?old?=?System.currentTimeMillis(); byte[]?buff?=?person.toByteArray(); System.out.println("ProtoBuf 編碼耗時:"?+?(System.currentTimeMillis()?-?old)); System.out.println(Arrays.toString(buff)); System.out.println("ProtoBuf?數據長度:"?+?buff.length); 6.?將ProtoBuf數據,轉換回Java對象 System.out.println("-開始解碼-"); old?=?System.currentTimeMillis(); Message.Person?personOut?=?Message.Person.parseFrom(buff); System.out.println("ProtoBuf 解碼耗時:"?+?(System.currentTimeMillis()?-?old)); System.out.printf("Id:%d,?Name:%s ",?personOut.getId(),?personOut.getName()); List?phoneList?=?personOut.getPhoneList(); for?(Message.Person.Phone?phone?:?phoneList) { ????System.out.printf("手機號:%s?(%s) ",?phone.getNumber(),?phone.getType()); }
6. 將ProtoBuf數據,轉換回Java對象
System.out.println("-開始解碼-"); old?=?System.currentTimeMillis(); Message.Person?personOut?=?Message.Person.parseFrom(buff); System.out.println("ProtoBuf 解碼耗時:"?+?(System.currentTimeMillis()?-?old)); System.out.printf("Id:%d,?Name:%s ",?personOut.getId(),?personOut.getName()); List?phoneList?=?personOut.getPhoneList(); for?(Message.Person.Phone?phone?:?phoneList) { ????System.out.printf("手機號:%s?(%s) ",?phone.getNumber(),?phone.getType()); }
比較
為了能體現ProtoBuf的優勢,我寫了同樣結構體的Java類,并且將Java對象轉換成JSON數據,來與ProtoBuf進行比較。JSON編譯庫使用Google提供的GSON庫,JSON的部分代碼就不貼出來了,直接展示結果
比較結果結果
運行 1 次
【?JSON?開始編碼?】 JSON 編碼1次,耗時:22ms JSON?數據長度:106 -開始解碼- JSON 解碼1次,耗時:1ms 【?ProtoBuf?開始編碼?】 ProtoBuf 編碼1次,耗時:32ms ProtoBuf?數據長度:34 -開始解碼- ProtoBuf 解碼1次,耗時:3ms
運行 10 次
【?JSON?開始編碼?】 JSON 編碼10次,耗時:22ms JSON?數據長度:106 -開始解碼- JSON 解碼10次,耗時:4ms 【?ProtoBuf?開始編碼?】 ProtoBuf 編碼10次,耗時:29ms ProtoBuf?數據長度:34 -開始解碼- ProtoBuf 解碼10次,耗時:3ms
運行 100 次
【?JSON?開始編碼?】 JSON 編碼100次,耗時:32ms JSON?數據長度:106 -開始解碼- JSON 解碼100次,耗時:8ms 【?ProtoBuf?開始編碼?】 ProtoBuf 編碼100次,耗時:31ms ProtoBuf?數據長度:34 -開始解碼- ProtoBuf 解碼100次,耗時:4ms
運行 1000 次
【?JSON?開始編碼?】 JSON 編碼1000次,耗時:39ms JSON?數據長度:106 -開始解碼- JSON 解碼1000次,耗時:21ms 【?ProtoBuf?開始編碼?】 ProtoBuf 編碼1000次,耗時:37ms ProtoBuf?數據長度:34 -開始解碼- ProtoBuf 解碼1000次,耗時:8ms
運行 1萬 次
【?JSON?開始編碼?】 JSON 編碼10000次,耗時:126ms JSON?數據長度:106 -開始解碼- JSON 解碼10000次,耗時:93ms 【?ProtoBuf?開始編碼?】 ProtoBuf 編碼10000次,耗時:49ms ProtoBuf?數據長度:34 -開始解碼- ProtoBuf 解碼10000次,耗時:23ms
運行 10萬 次
【?JSON?開始編碼?】 JSON 編碼100000次,耗時:248ms JSON?數據長度:106 -開始解碼- JSON 解碼100000次,耗時:180ms 【?ProtoBuf?開始編碼?】 ProtoBuf 編碼100000次,耗時:51ms ProtoBuf?數據長度:34 -開始解碼- ProtoBuf 解碼100000次,耗時:58ms
總結
編解碼性能
上述栗子只是簡單的采樣,實際上據我的實驗發現
次數在1千以下,ProtoBuf 的編碼與解碼性能,都與JSON不相上下,甚至還有比JSON差的趨勢。
次數在2千以上,ProtoBuf的編碼解碼性能,都比JSON高出很多。
次數在10萬以上,ProtoBuf的編解碼性能就很明顯了,遠遠高出JSON的性能。
內存占用
ProtoBuf的內存34,而JSON到達106 ,ProtoBuf的內存占用只有JSON的1/3.
結尾
其實這次實驗有很多可待優化的地方,就算是這種粗略的測試,也能看出來ProtoBuf的優勢。
兼容
新增字段
在proto文件中新增 nickname 字段
生成Java文件
用老proto字節數組數據,轉換成對象
Id:1994,?Name:XIAOLEI 手機號:100860?(HOME) 手機號:100100?(MOBILE) getNickname=
結果,是可以轉換成功。
刪除字段
在proto文件中刪除 name 字段
生成Java文件
用老proto字節數組數據,轉換成對象
Id:1994,?Name:null 手機號:100860?(HOME) 手機號:100100?(MOBILE)
結果,是可以轉換成功。
編輯:黃飛
?
評論
查看更多