當(dāng)涉及到網(wǎng)絡(luò)通信和數(shù)據(jù)存儲(chǔ)時(shí),數(shù)據(jù)序列化 一直都是一個(gè)重要的話題;特別是現(xiàn)在很多公司都在推行微服務(wù),數(shù)據(jù)序列化更是重中之重,通常會(huì)選擇使用 JSON 作為數(shù)據(jù)交換格式,且 JSON 已經(jīng)成為業(yè)界的主流。但是 Google 這么大的公司使用的卻是一種被稱為 Protobuf 的數(shù)據(jù)交換格式,它是有什么優(yōu)勢(shì)嗎?這篇文章介紹 Protobuf 的相關(guān)知識(shí)。
GitHub:https://github.com/protocolbuffers/protobuf
官方文檔:https://v/overview/
Protobuf 介紹 Protobuf (Protocol Buffers)是由 Google 開發(fā)的一種輕量級(jí)、高效 的數(shù)據(jù)交換格式,它被用于結(jié)構(gòu)化數(shù)據(jù)的序列化、反序列化和傳輸。相比于 XML 和 JSON 等文本格式,Protobuf 具有更小的數(shù)據(jù)體積、更快的解析速度和更強(qiáng)的可擴(kuò)展性。
Protobuf 的核心思想是使用協(xié)議(Protocol)來(lái)定義數(shù)據(jù)的結(jié)構(gòu)和編碼方式 。使用 Protobuf,可以先定義數(shù)據(jù)的結(jié)構(gòu)和各字段的類型、字段等信息,然后使用Protobuf提供的編譯器生成對(duì)應(yīng)的代碼 ,用于序列化和反序列化數(shù)據(jù) 。由于 Protobuf 是基于二進(jìn)制編碼的,因此可以在數(shù)據(jù)傳輸和存儲(chǔ)中實(shí)現(xiàn)更高效的數(shù)據(jù)交換,同時(shí)也可以跨語(yǔ)言 使用。
相比于 XML 和 JSON,Protobuf 有以下幾個(gè)優(yōu)勢(shì) :
· 更小的數(shù)據(jù)量 :Protobuf 的二進(jìn)制編碼通常比 XML 和 JSON 小 3-10 倍,因此在網(wǎng)絡(luò)傳輸和存儲(chǔ)數(shù)據(jù)時(shí)可以節(jié)省帶寬和存儲(chǔ)空間。
· 更快的序列化和反序列化速度 :由于 Protobuf 使用二進(jìn)制格式,所以序列化和反序列化速度比 XML 和 JSON 快得多。
· 跨語(yǔ)言 :Protobuf 支持多種編程語(yǔ)言,可以使用不同的編程語(yǔ)言來(lái)編寫客戶端和服務(wù)端。這種跨語(yǔ)言的特性使得 Protobuf 受到很多開發(fā)者的歡迎(JSON 也是如此)。
· 易于維護(hù)可擴(kuò)展 :Protobuf 使用 .proto 文件定義數(shù)據(jù)模型和數(shù)據(jù)格式,這種文件比 XML 和 JSON 更容易閱讀和維護(hù),且可以在不破壞原有協(xié)議的基礎(chǔ)上,輕松添加或刪除字段,實(shí)現(xiàn)版本升級(jí)和兼容性。
編寫 Protobuf 使用 Protobuf 的語(yǔ)言定義文件(.proto)可以定義要傳輸?shù)男畔⒌臄?shù)據(jù)結(jié)構(gòu),可以包括各個(gè)字段的名稱、類型等信息。同時(shí)也可以相互嵌套組合,構(gòu)造出更加復(fù)雜的消息結(jié)構(gòu)。
比如想要構(gòu)造一個(gè)地址簿 AddressBook 信息結(jié)構(gòu)。一個(gè) AddressBook 可以包含多個(gè)人員 Person 信息,每個(gè) Person 信息可以包含 id、name、email 信息,同時(shí)一個(gè) Person 也可以包含多個(gè)電話號(hào)碼信息 PhoneNumber,每個(gè)電話號(hào)碼信息需要指定號(hào)碼種類,如手機(jī)、家庭電話、工作電話等。
如果使用 Protobuf 編寫定義文件如下:
// 文件:addressbook.proto syntax = 'proto3' ;// 指定 protobuf 包名,防止有相同類名的 message 定義 package com.wdbyte.protobuf;// 是否生成多個(gè)文件 option java_multiple_files = true ;// 生成的文件存放在哪個(gè)包下 option java_package = 'com.wdbyte.tool.protos' ;// 生成的類名,如果沒(méi)有指定,會(huì)根據(jù)文件名自動(dòng)轉(zhuǎn)駝峰來(lái)命名 option java_outer_classname = 'AddressBookProtos' ; message Person { // =1,=2 作為序列化后的二進(jìn)制編碼中的字段的唯一標(biāo)簽,也因此,1-15 比 16 會(huì)少一個(gè)字節(jié),所以盡量使用 1-15 來(lái)指定常用字段。 optional int32 id = 1 ; optional string name = 2 ; optional string email = 3 ; enum PhoneType { MOBILE = 0 ; HOME = 1 ; WORK = 2 ; } message PhoneNumber { optional string number = 1 ; optional PhoneType type = 2 ; } repeated PhoneNumber phones = 4 ; } message AddressBook { repeated Person people = 1 ; }
Protobuf 文件中的語(yǔ)法解釋。
頭部全局定義 · syntax = 'proto3';
指定 Protobuf 版本為版本3(最新版本)
· package com.wdbyte.protobuf;
指定 Protobuf 包名,防止有相同類名的 message
定義,這個(gè)包名是生成的類中所用到的一些信息的前綴,并非類所在包。
· option java_multiple_files = true;
是否生成多個(gè)文件。若 false
,則只會(huì)生成一個(gè)類,其他類以內(nèi)部類形式提供。
· option java_package =
生成的類所在包。
· option java_outer_classname
生成的類名,若無(wú),自動(dòng)使用文件名進(jìn)行駝峰轉(zhuǎn)換來(lái)為類命名。
消息結(jié)構(gòu)具體定義 message Person
定一個(gè)了一個(gè) Person 類。
Person 類中的字段被 optional
修飾,被 optional
修飾說(shuō)明字段可以不賦值。
· 修飾符 optional
表示可選字段,可以不賦值。
· 修飾符 repeated
表示數(shù)據(jù)重復(fù)多個(gè),如數(shù)組,如 List。
· 修飾符 required
表示必要字段,必須給值,否則會(huì)報(bào)錯(cuò) RuntimeException
,但是在 Protobuf 版本 3 中被移除。即使在版本 2 中也應(yīng)該慎用,因?yàn)橐坏┒x,很難更改。
字段類型定義 修飾符后面緊跟的是字段類型,如 int32
、string
。常用的類型如下:
· int32、int64、uint32、uint64
:整數(shù)類型,包括有符號(hào)和無(wú)符號(hào)類型。
· float、double
:浮點(diǎn)數(shù)類型。
· bool
:布爾類型,只有兩個(gè)值,true 和 false。
· string
:字符串類型。
· bytes
:二進(jìn)制數(shù)據(jù)類型。
· enum
:枚舉類型,枚舉值可以是整數(shù)或字符串。
· message
:消息類型,可以嵌套其他消息類型,類似于結(jié)構(gòu)體。
字段后面的 =1,=2
是作為序列化后的二進(jìn)制編碼中的字段的對(duì)應(yīng)標(biāo)簽,因?yàn)?Protobuf 消息在序列化后是不包含字段信息的,只有對(duì)應(yīng)的字段序號(hào),所以節(jié)省了空間 。也因此,1-15 比 16 會(huì)少一個(gè)字節(jié),所以盡量使用 1-15 來(lái)指定常用字段 。且一旦定義,不要隨意更改,否則可能會(huì)對(duì)不上序列化信息 。
編譯 Protobuf 使用 Protobuf 提供的編譯器,可以將 .proto
文件編譯成各種語(yǔ)言的代碼文件(如 Java、C++、Python 等)。
下載編譯器:https://github.com/protocolbuffers/protobuf/releases/latest
安裝完成后可以使用 protoc
命令編譯 proto
文件,如編譯示例中的 addressbook.proto
.
protoc --java_out=./java ./resources/addressbook.proto# --java_out 指定輸出 java 格式文件,輸出到 ./java 目錄# ./resources/addressbook.proto 為 proto 文件位置
生成后可以看到生產(chǎn)的類文件。
./ ├── java │ └── com │ └── wdbyte │ └── tool │ ├── protos │ │ ├── AddressBook.java │ │ ├── AddressBookOrBuilder.java │ │ ├── AddressBookProtos.java │ │ ├── Person.java │ │ ├── PersonOrBuilder.java └── resources ├── addressbook.proto
使用 Protobuf 使用 Java 語(yǔ)言操作 Protobuf,首先需要引入 Protobuf 依賴。
Maven 依賴:
<dependency > <groupId >com.google.protobuf</groupId > <artifactId >protobuf-java</artifactId > <version >3.22.3</version > </dependency >
構(gòu)造消息對(duì)象 // 直接構(gòu)建 PhoneNumber phoneNumber1 = PhoneNumber.newBuilder().setNumber('18388888888' ).setType(PhoneType.HOME).build();Person person1 = Person.newBuilder().setId(1 ).setName('www.' ).setEmail('xxx@' ).addPhones(phoneNumber1).build();AddressBook addressBook1 = AddressBook.newBuilder().addPeople(person1).build(); System.out.println(addressBook1); System.out.println('------------------' );// 鏈?zhǔn)綐?gòu)建 AddressBook addressBook2 = AddressBook .newBuilder() .addPeople(Person.newBuilder() .setId(2 ) .setName('www.' ) .setEmail('yyy@126.com' ) .addPhones(PhoneNumber.newBuilder() .setNumber('18388888888' ) .setType(PhoneType.HOME) ) ) .build(); System.out.println(addressBook2);
輸出:
people { id: 1 name: 'www.' email: 'xxx@' phones { number: '18388888888' type: HOME } } ------------------ people { id: 2 name: 'www.' email: 'yyy@126.com' phones { number: '18388888888' type: HOME } }
序列化、反序列化 序列化:將內(nèi)存中的數(shù)據(jù)對(duì)象序列化為二進(jìn)制數(shù)據(jù),可以用于網(wǎng)絡(luò)傳輸或存儲(chǔ)等場(chǎng)景。
反序列化:將二進(jìn)制數(shù)據(jù)反序列化成內(nèi)存中的數(shù)據(jù)對(duì)象,可以用于數(shù)據(jù)處理和業(yè)務(wù)邏輯。
下面演示使用 Protobuf 進(jìn)行字符數(shù)組和文件的序列化及反序列化過(guò)程。
package com.wdbyte.tool.protos;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;/** * * @author www. */ public class ProtobufTest2 { public static void main (String[] args) throws IOException { PhoneNumber phoneNumber1 = PhoneNumber.newBuilder().setNumber('18388888888' ).setType(PhoneType.HOME).build(); Person person1 = Person.newBuilder().setId(1 ).setName('www.' ).setEmail('xxx@' ).addPhones(phoneNumber1).build(); AddressBook addressBook1 = AddressBook.newBuilder().addPeople(person1).build(); // 序列化成字節(jié)數(shù)組 byte [] byteArray = addressBook1.toByteArray(); // 反序列化 - 字節(jié)數(shù)組轉(zhuǎn)對(duì)象 AddressBook addressBook2 = AddressBook.parseFrom(byteArray); System.out.println('字節(jié)數(shù)組反序列化:' ); System.out.println(addressBook2); // 序列化到文件 addressBook1.writeTo(new FileOutputStream ('AddressBook1.txt' )); // 讀取文件反序列化 AddressBook addressBook3 = AddressBook.parseFrom(new FileInputStream ('AddressBook1.txt' )); System.out.println('文件讀取反序列化:' ); System.out.println(addressBook3); } }
輸出:
字節(jié)數(shù)組反序列化: people { id: 1 name: 'www.' email: 'xxx@' phones { number: '18388888888' type: HOME } } 文件讀取反序列化: people { id: 1 name: 'www.' email: 'xxx@' phones { number: '18388888888' type: HOME } }
Protobuf 為什么高效 在分析 Protobuf 高效之前,我們先確認(rèn)一下 Protobuf 是否真的高效,下面將 Protobuf 與 JSON 進(jìn)行對(duì)比,分別對(duì)比序列化和反序列化速度 以及序列化后的存儲(chǔ)占用大小 。
測(cè)試工具:JMH,F(xiàn)astJSON,
測(cè)試對(duì)象:Protobuf 的 addressbook.proto
,JSON 的普通 Java 類。
Maven 依賴:
<dependency > <groupId >com.alibaba</groupId > <artifactId >fastjson</artifactId > <version >2.0.7</version > </dependency > <dependency > <groupId >org.openjdk.jmh</groupId > <artifactId >jmh-core</artifactId > <version >1.33</version > </dependency > <dependency > <groupId >org.openjdk.jmh</groupId > <artifactId >jmh-generator-annprocess</artifactId > <version >1.33</version > <scope >provided</scope > </dependency >
先編寫與addressbook.proto
結(jié)構(gòu)相同的 Java 類 AddressBookJava.java
.
public class AddressBookJava { List<PersonJava> personJavaList; public static class PersonJava { private int id; private String name; private String email; private PhoneNumberJava phones; // get...set... } public static class PhoneNumberJava { private String number; private PhoneTypeJava phoneTypeJava; // get....set.... } public enum PhoneTypeJava { MOBILE, HOME, WORK; } public List<PersonJava> getPersonJavaList () { return personJavaList; } public void setPersonJavaList (List<PersonJava> personJavaList) { this .personJavaList = personJavaList; } }
序列化大小對(duì)比 分別在地址簿中添加 1000 個(gè)人員信息,輸出序列化后的數(shù)組大小。
package com.wdbyte.tool.protos;import java.io.IOException;import java.util.ArrayList;import com.alibaba.fastjson.JSON;import com.wdbyte.tool.protos.AddressBook.Builder;import com.wdbyte.tool.protos.AddressBookJava.PersonJava;import com.wdbyte.tool.protos.AddressBookJava.PhoneNumberJava;import com.wdbyte.tool.protos.AddressBookJava.PhoneTypeJava;import com.wdbyte.tool.protos.Person.PhoneNumber;import com.wdbyte.tool.protos.Person.PhoneType;/** * @author https://www. */ public class ProtobufTest3 { public static void main (String[] args) throws IOException { AddressBookJava addressBookJava = createAddressBookJava(1000 ); String jsonString = JSON.toJSONString(addressBookJava); System.out.println('json string size:' + jsonString.length()); AddressBook addressBook = createAddressBook(1000 ); byte [] addressBookByteArray = addressBook.toByteArray(); System.out.println('protobuf byte array size:' + addressBookByteArray.length); } public static AddressBook createAddressBook (int personCount) { Builder builder = AddressBook.newBuilder(); for (int i = 0 ; i < personCount; i++) { builder.addPeople(Person.newBuilder() .setId(i) .setName('www.' ) .setEmail('xxx@126.com' ) .addPhones(PhoneNumber.newBuilder() .setNumber('18333333333' ) .setType(PhoneType.HOME) ) ); } return builder.build(); } public static AddressBookJava createAddressBookJava (int personCount) { AddressBookJava addressBookJava = new AddressBookJava (); addressBookJava.setPersonJavaList(new ArrayList <>()); for (int i = 0 ; i < personCount; i++) { PersonJava personJava = new PersonJava (); personJava.setId(i); personJava.setName('www.' ); personJava.setEmail('xxx@126.com' ); PhoneNumberJava numberJava = new PhoneNumberJava (); numberJava.setNumber('18333333333' ); numberJava.setPhoneTypeJava(PhoneTypeJava.HOME); personJava.setPhones(numberJava); addressBookJava.getPersonJavaList().add(personJava); } return addressBookJava; } }
輸出:
json string size:108910 protobuf byte array size:50872
可見測(cè)試中 Protobuf 的序列化結(jié)果只有 JSON 的一半左右。
序列化速度對(duì)比 使用 JMH 進(jìn)行性能測(cè)試,分別測(cè)試 JSON 的序列化和反序列以及 Protobuf 的序列化和反序列化性能情況。每次測(cè)試前進(jìn)行 3 次預(yù)熱,每次 3 秒。接著進(jìn)行 5 次測(cè)試,每次 3 秒,收集測(cè)試情況。
package com.wdbyte.tool.protos;import java.util.ArrayList;import java.util.concurrent.TimeUnit;import com.alibaba.fastjson.JSON;import com.google.protobuf.InvalidProtocolBufferException;import com.wdbyte.tool.protos.AddressBook.Builder;import com.wdbyte.tool.protos.AddressBookJava.PersonJava;import com.wdbyte.tool.protos.AddressBookJava.PhoneNumberJava;import com.wdbyte.tool.protos.AddressBookJava.PhoneTypeJava;import com.wdbyte.tool.protos.Person.PhoneNumber;import com.wdbyte.tool.protos.Person.PhoneType;import org.openjdk.jmh.annotations.Benchmark;import org.openjdk.jmh.annotations.BenchmarkMode;import org.openjdk.jmh.annotations.Fork;import org.openjdk.jmh.annotations.Measurement;import org.openjdk.jmh.annotations.Mode;import org.openjdk.jmh.annotations.OutputTimeUnit;import org.openjdk.jmh.annotations.Scope;import org.openjdk.jmh.annotations.Setup;import org.openjdk.jmh.annotations.State;import org.openjdk.jmh.annotations.Warmup;/** * @author https://www. */ @State(Scope.Thread) @Fork(2) @Warmup(iterations = 3, time = 3) @Measurement(iterations = 5, time = 3) @BenchmarkMode(Mode.Throughput) // Throughput:吞吐量,SampleTime:采樣時(shí)間 @OutputTimeUnit(TimeUnit.MILLISECONDS) public class ProtobufTest4 { private AddressBookJava addressBookJava; private AddressBook addressBook; @Setup public void init () { addressBookJava = createAddressBookJava(1000 ); addressBook = createAddressBook(1000 ); } @Benchmark public AddressBookJava testJSON () { // 轉(zhuǎn) JSON String jsonString = JSON.toJSONString(addressBookJava); // JSON 轉(zhuǎn)對(duì)象 return JSON.parseObject(jsonString, AddressBookJava.class); } @Benchmark public AddressBook testProtobuf () throws InvalidProtocolBufferException { // 轉(zhuǎn) JSON byte [] addressBookByteArray = addressBook.toByteArray(); // JSON 轉(zhuǎn)對(duì)象 return AddressBook.parseFrom(addressBookByteArray); } public static AddressBook createAddressBook (int personCount) { Builder builder = AddressBook.newBuilder(); for (int i = 0 ; i < personCount; i++) { builder.addPeople(Person.newBuilder() .setId(i) .setName('www.' ) .setEmail('xxx@126.com' ) .addPhones(PhoneNumber.newBuilder() .setNumber('18333333333' ) .setType(PhoneType.HOME) ) ); } return builder.build(); } public static AddressBookJava createAddressBookJava (int personCount) { AddressBookJava addressBookJava = new AddressBookJava (); addressBookJava.setPersonJavaList(new ArrayList <>()); for (int i = 0 ; i < personCount; i++) { PersonJava personJava = new PersonJava (); personJava.setId(i); personJava.setName('www.' ); personJava.setEmail('xxx@126.com' ); PhoneNumberJava numberJava = new PhoneNumberJava (); numberJava.setNumber('18333333333' ); numberJava.setPhoneTypeJava(PhoneTypeJava.HOME); personJava.setPhones(numberJava); addressBookJava.getPersonJavaList().add(personJava); } return addressBookJava; } }
JMH 吞吐量測(cè)試結(jié)果(Score 值越大吞吐量越高,性能越好):
Benchmark Mode Cnt Score Error Units ProtobufTest3.testJSON thrpt 10 1.877 ± 0.287 ops/ms ProtobufTest3.testProtobuf thrpt 10 2.813 ± 0.446 ops/ms
JMH 采樣時(shí)間測(cè)試結(jié)果(Score 越小,采樣時(shí)間越小,性能越好):
Benchmark Mode Cnt Score Error Units ProtobufTest3.testJSON sample 53028 0.565 ± 0.005 ms/op ProtobufTest3.testProtobuf sample 90413 0.332 ± 0.001 ms/op
從測(cè)試結(jié)果看,不管是吞吐量測(cè)試,還是采樣時(shí)間測(cè)試,Protobuf 都優(yōu)于 JSON。
為什么高效? Protobuf 是如何實(shí)現(xiàn)這種高效緊湊的數(shù)據(jù)編碼和解碼的呢?
首先,Protobuf 使用二進(jìn)制編碼,會(huì)提高性能;其次 Protobuf 在將數(shù)據(jù)轉(zhuǎn)換成二進(jìn)制時(shí),會(huì)對(duì)字段和類型重新編碼,減少空間占用。它采用 TLV
格式來(lái)存儲(chǔ)編碼后的數(shù)據(jù)。TLV
也是就是 Tag-Length-Value ,是一種常見的編碼方式,因?yàn)閿?shù)據(jù)其實(shí)都是鍵值對(duì)形式,所以在 TAG
中會(huì)存儲(chǔ)對(duì)應(yīng)的字段和類型 信息,Length
存儲(chǔ)內(nèi)容的長(zhǎng)度,Value
存儲(chǔ)具體的內(nèi)容。
還記得上面定義結(jié)構(gòu)體時(shí)每個(gè)字段都對(duì)應(yīng)一個(gè)數(shù)字嗎?如 =1
,=2
,=3
.
message Person { optional int32 id = 1; optional string name = 2; optional string email = 3; }
在序列化成二進(jìn)制時(shí)候就是通過(guò)這個(gè)數(shù)字來(lái)標(biāo)記對(duì)應(yīng)的字段的,二進(jìn)制中只存儲(chǔ)這個(gè)數(shù)字,反序列化時(shí)通過(guò)這個(gè)數(shù)字找對(duì)應(yīng)的字段。這也是上面為什么說(shuō)盡量使用 1-15 范圍內(nèi)的數(shù)字,因?yàn)橐坏┏^(guò) 15,就需要多一個(gè) bit 位來(lái)存儲(chǔ)。
那么類型信息呢?比如 int32
怎么標(biāo)記,因?yàn)轭愋蛡€(gè)數(shù)有限,所以 Protobuf 規(guī)定了每個(gè)類型對(duì)應(yīng)的二進(jìn)制編碼,比如 int32
對(duì)應(yīng)二進(jìn)制 000
,string
對(duì)應(yīng)二進(jìn)制 010
,這樣就可以只用三個(gè)比特位存儲(chǔ)類型信息。
這里只是舉例描述大概思想,具體還有一些變化。
詳情可以參考官方文檔:https://v/programming-guides/encoding/
其次,Protobuf 還會(huì)采用一種變長(zhǎng)編碼的方式來(lái)存儲(chǔ)數(shù)據(jù) 。這種編碼方式能夠保證數(shù)據(jù)占用的空間最小化,從而減少了數(shù)據(jù)傳輸和存儲(chǔ)的開銷。具體來(lái)說(shuō),Protobuf 會(huì)將整數(shù)和浮點(diǎn)數(shù)等類型變換成一個(gè)或多個(gè)字節(jié)的形式,其中每個(gè)字節(jié)都包含了一部分?jǐn)?shù)據(jù)信息和一部分標(biāo)識(shí)符信息。這種編碼方式可以在數(shù)據(jù)值比較小的情況下,只使用一個(gè)字節(jié)來(lái)存儲(chǔ)數(shù)據(jù) ,以此來(lái)提高編碼效率。
最后,Protobuf 還可以通過(guò)采用壓縮算法來(lái)減少數(shù)據(jù)傳輸?shù)拇笮?/strong>。比如 GZIP 算法能夠?qū)⒃紨?shù)據(jù)壓縮成更小的二進(jìn)制格式,從而在網(wǎng)絡(luò)傳輸中能夠節(jié)省帶寬和傳輸時(shí)間。Protobuf 還提供了一些可選的壓縮算法,如 zlib 和 snappy,這些算法在不同的場(chǎng)景下能夠適應(yīng)不同的壓縮需求。
綜上所述,Protobuf 在實(shí)現(xiàn)高效編碼和解碼的過(guò)程中,采用了多種優(yōu)化方式,從而在實(shí)際應(yīng)用中能夠有效地提升數(shù)據(jù)傳輸和處理的效率。
總結(jié) ProtoBuf 是一種輕量、高效 的數(shù)據(jù)交換格式,它具有以下優(yōu)點(diǎn):
· 語(yǔ)言中立 ,可以支持多種編程語(yǔ)言;
· 數(shù)據(jù)結(jié)構(gòu)清晰,易于維護(hù)和擴(kuò)展;
· 二進(jìn)制編碼,數(shù)據(jù)體積小,傳輸效率高 ;
· 自動(dòng)生成代碼,開發(fā)效率高。
但是,ProtoBuf 也存在以下缺點(diǎn):
· 學(xué)習(xí)成本較高,需要掌握其語(yǔ)法規(guī)則和使用方法;
· 需要先定義數(shù)據(jù)結(jié)構(gòu),然后才能對(duì)數(shù)據(jù)進(jìn)行序列化和反序列化,增加了一定的開發(fā)成本;
· 由于二進(jìn)制編碼,可讀性較差,這點(diǎn)不如 JSON 可以直接閱讀 。
總體來(lái)說(shuō),Protobuf 適合用于數(shù)據(jù)傳輸和存儲(chǔ)等場(chǎng)景,能夠提高數(shù)據(jù)傳輸效率和減少數(shù)據(jù)體積 。但對(duì)于需要人類可讀的數(shù)據(jù),或需要實(shí)時(shí)修改的數(shù)據(jù),或者對(duì)數(shù)據(jù)的傳輸效率和體積沒(méi)那么在意的場(chǎng)景,選擇更加通用的 JSON 未嘗不是一個(gè)好的選擇。
參考:https://v/overview/
引用鏈接 [1]
Github.com/niumoo/javaNotes: https://github.com/niumoo/JavaNotes/tree/master/tool-java-apache-httpclient