通用性:通用性是指序列化框架是否支持跨語(yǔ)言、跨平臺(tái)。
成都創(chuàng)新互聯(lián)公司主營(yíng)儋州網(wǎng)站建設(shè)的網(wǎng)絡(luò)公司,主營(yíng)網(wǎng)站建設(shè)方案,APP應(yīng)用開發(fā),儋州h5微信小程序定制開發(fā)搭建,儋州網(wǎng)站營(yíng)銷推廣歡迎儋州等地區(qū)企業(yè)咨詢易用性:易用性是指序列化框架是否便于使用、調(diào)試,會(huì)影響開發(fā)效率。
可擴(kuò)展性:隨著業(yè)務(wù)的發(fā)展,傳輸實(shí)體可能會(huì)發(fā)生變化,但是舊實(shí)體有可能還會(huì)被使用。這時(shí)候就需要考慮所選擇的序列化框架是否具有良好的擴(kuò)展性。
性能:序列化性能主要包括時(shí)間開銷和空間開銷。序列化的數(shù)據(jù)通常用于持久化或網(wǎng)絡(luò)傳輸,所以其大小是一個(gè)重要的指標(biāo)。而編解碼時(shí)間同樣是影響序列化協(xié)議選擇的重要指標(biāo),因?yàn)槿缃竦南到y(tǒng)都在追求高性能。
Java數(shù)據(jù)類型和語(yǔ)法支持:不同序列化框架所能夠支持的數(shù)據(jù)類型以及語(yǔ)法結(jié)構(gòu)是不同的。這里我們要對(duì)Java的數(shù)據(jù)類型和語(yǔ)法特性進(jìn)行測(cè)試,來(lái)看看不同序列化框架對(duì)Java數(shù)據(jù)類型和語(yǔ)法結(jié)構(gòu)的支持度。
框架 | JDK序列化 | FST | Kryo | Protobuf(注2) | Thift | Hession | Avro |
---|---|---|---|---|---|---|---|
通用性 | 不支持 | 不支持 | 支持但非常復(fù)雜 | 非常通用 | 非常通用 | 支持跨語(yǔ)言RPC通信 | 非常通用 |
易用性 | 略復(fù)雜 | 語(yǔ)法簡(jiǎn)潔 | 語(yǔ)法簡(jiǎn)潔 | 比較簡(jiǎn)單 | 比較簡(jiǎn)單 | 非常簡(jiǎn)單 | 略復(fù)雜 |
可擴(kuò)展性 | 可擴(kuò)展(注1) | 略復(fù)雜 | 支持 | 友好支持 | 友好支持 | 友好支持 | 略復(fù)雜 |
性能 | 耗時(shí)久,字節(jié)多 | 速度快,字節(jié)少 | 速度快,字節(jié)少 | 略低于前FST,Kryo | 和Protobuf類型 | 速度快,字節(jié)少 | 速度快,字節(jié)少 |
數(shù)據(jù)類型和語(yǔ)法支持 | 基本支持 | 基本支持 | 基本支持 | 不支持定義Java方法 | 不支持定義Java方法 | 基本支持 | 不支持定義Java方法 |
序列化框架 | 通用性 |
---|---|
JDK Serializer | 使用語(yǔ)法過(guò)于生硬 |
FST | 使用簡(jiǎn)潔,F(xiàn)STConfiguration提供了序列化與反序列化的方法 |
Kryo | 使用簡(jiǎn)潔,Input/Output封裝了幾乎所有能有需要的流方法 |
Protocol buffer | 稍微復(fù)雜。需要編寫所需序列化類的proto文件,然后編譯生成Java代碼。但是自動(dòng)生成Java類,包含了序列化與反序列化方法 |
Thrift | 稍微復(fù)雜。需要編寫所需的序列化類的thrift文件,然后編譯生成Java代碼。然后通過(guò)TSerializer和TDserializer進(jìn)行序列化與反序列化 |
Hessian | 使用簡(jiǎn)單,在跨語(yǔ)言的基礎(chǔ)上不需要使用IDL |
Avro | 使用較復(fù)雜。相較于Protobuf和Thrift來(lái)說(shuō),對(duì)于一些靜態(tài)語(yǔ)言無(wú)序生成代碼。但是對(duì)于Java來(lái)一般還需要生成代碼,并且Avro提供的API不是很友好 |
序列化框架 | 易用性 |
---|---|
JDK Serializer | 使用語(yǔ)法過(guò)于生硬 |
FST | 使用簡(jiǎn)潔,F(xiàn)STConfiguration提供了序列化與反序列化的方法 |
Kryo | 使用簡(jiǎn)潔,Input/Output封裝了幾乎所有能有需要的流方法 |
Protocol buffer | 稍微復(fù)雜。需要編寫所需序列化類的proto文件,然后編譯生成Java代碼。但是自動(dòng)生成Java類,包含了序列化與反序列化方法 |
Thrift | 稍微復(fù)雜。需要編寫所需的序列化類的thrift文件,然后編譯生成Java代碼。然后通過(guò)TSerializer和TDserializer進(jìn)行序列化與反序列化 |
Hessian | 使用簡(jiǎn)單,在跨語(yǔ)言的基礎(chǔ)上不需要使用IDL |
Avro | 使用較復(fù)雜。相較于Protobuf和Thrift來(lái)說(shuō),對(duì)于一些靜態(tài)語(yǔ)言無(wú)序生成代碼。但是對(duì)于Java來(lái)一般還需要生成代碼,并且Avro提供的API不是很友好 |
序列化框架 | 可擴(kuò)展性 |
---|---|
JDK Serializer | 自定義serialVersionUID,保證序列化前后VUID一致即可 |
FST | 通過(guò)@Version控制版本,新增字段需要修改Version版本 |
Kryo | 默認(rèn)序列化器不支持字段擴(kuò)展,需要修改默認(rèn)序列化器或自己實(shí)現(xiàn)序列化器 |
Protocol buffer | 支持字段擴(kuò)展,只要保證新增id標(biāo)識(shí)沒有使用過(guò)即可 |
Thrift | 支持字段擴(kuò)展。新增字段為required類型時(shí),需要設(shè)置默認(rèn)值 |
Hessian | 支持字段擴(kuò)展 |
Avro | 支持字段擴(kuò)展。注意需要為字段設(shè)置默認(rèn)值 |
序列化大小對(duì)比
對(duì)比各個(gè)序列化框架序列化后的數(shù)據(jù)大小如下,可以看出kryo preregister(預(yù)先注冊(cè)序列化類)和Avro序列化結(jié)果都很不錯(cuò)。所以,如果在序列化大小上有需求,可以選擇Kryo或Avro。
序列化時(shí)間開銷對(duì)比
序列化與反序列化的時(shí)間開銷,kryo preregister和fst preregister都能提供優(yōu)異的性能,其中fst pre序列化時(shí)間就最佳,而kryo pre在序列化和反序列化時(shí)間開銷上基本一致。所以,如果序列化時(shí)間是主要的考慮指標(biāo),可以選擇Kryo或FST,都能提供不錯(cuò)的性能體驗(yàn)。
JDK Serializable中通過(guò)serialVersionUID控制序列化類的版本,如果序列化與反序列化版本不一致,則會(huì)拋出java.io.InvalidClassException異常信息,提示序列化與反序列化SUID不一致。
java.io.InvalidClassException: com.yjz.serialization.java.UserInfo; local class incompatible: stream classdesc serialVersionUID = -5548195544707231683, local class serialVersionUID = -5194320341014913710
上面這種情況,是由于我們沒有定義serialVersionUID,而是由JDK自動(dòng)hash生成的,所以序列化與反序列化前后結(jié)果不一致。
但是我們可以通過(guò)自定義serialVersionUID方式來(lái)規(guī)避掉這種情況(序列化前后都是使用定義的serialVersionUID),這樣JDK Serializable就可以支持字段擴(kuò)展了。
private static final long serialVersionUID = 1L;
Protocol buffer是一種語(yǔ)言中立、平臺(tái)無(wú)關(guān)、可擴(kuò)展的序列化框架。Protocol buffer相較于前面幾種序列化框架而言,它是需要預(yù)先定義Schema的。
下面是使用Protobuf的Demo:
(1)編寫proto描述文件:
syntax = "proto3";
option java_package = "com.yjz.serialization.protobuf3";
message MessageInfo
{
string username = 1;
string password = 2;
int32 age = 3;
map params = 4;
}
(2)生成Java代碼:
protoc --java_out=./src/main/java message.proto
(3)生成的Java代碼,已經(jīng)自帶了編解碼方法:
//編碼
byte[] bytes = MessageInfo.toByteArray()
//解碼
MessageInfo messageInfo = Message.MessageInfo.parseFrom(bytes);