激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - 編程技術 - 用數據說話,序列化框架測評報告

用數據說話,序列化框架測評報告

2022-03-02 22:13愛笑的架構師雷小帥 編程技術

今天選擇幾款市面上常用的序列化框架進行測試對比,幫助開發團隊搞清楚不同場景該采用哪種序列化框架。

序列化是我們在日常開發中經常會使用到的技術,比如需要將內存對象持久化存儲、需要將對象通過網絡傳輸到遠端。目前市面上序列化框架非常多,開發團隊在進行技術選型時通常難以抉擇,甚至會踩坑。

今天選擇幾款市面上常用的序列化框架進行測試對比,幫助開發團隊搞清楚不同場景該采用哪種序列化框架。

測試對比的框架有四款:

JDK原生、fastjson、Kryo、Protobuf

接下來會從以下這四個方面給出詳細的測試對比結果:

(1)是否通用:是否支持跨語言、跨平臺;

(2)是否容易使用:是否編譯使用和調試;

(3)性能好不好:序列化性能主要包括時間開銷和空間開銷,時間開銷是指序列化和反序列化對象所耗費的時間,空間開銷是指序列化生成數據大小;

(4)可擴展強不強:隨著業務發展,傳輸的業務對象可能會發生變化,比如說新增字段,這個時候就要看所選用的序列化框架是否有良好的擴展性;

框架1:JDK原生

是否通用?

JDK 原生是 Java 自帶的序列化框架,與 Java 語言是強綁定的,通過 JDK 將對象序列化后是無法通過其他語言進行返序列化的,所以它的通用性比較差。

是否容易使用?

一個類實現了java.io.Serializable序列化接口就代表這個類的對象可以被序列化,否則就會報錯。

簡單認識一下Serializable這個類,通過看源碼我們知道Serializable僅僅是一個空接口,沒有定義任何方法。

public interface Serializable { } 

這說明Serializable僅僅是一個標識的作用,用來告訴 JVM 這個對象可以被序列化。

想真正完成對象序列化和反序列化還得借助 IO 核心操作類:ObjectOutputStream和ObjectInputStream。

  /**  * 序列化  *  * @param obj 待序列化對象  * @return 二進制字節數組  * @throws IOException  */ public static byte[] serialize(Object obj) throws IOException { // 字節輸出流
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); // 將對象序列化為二進制字節流
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream); objectOutputStream.writeObject(obj); // 獲取二進制字節數組
        byte[] bytes = byteArrayOutputStream.toByteArray(); // 關閉流
        objectOutputStream.close(); byteArrayOutputStream.close(); return bytes; } 

ObjectInputStream類的readObject()方法用于從 IO 流中讀取對象,完成對象反序列化:

  /**  * 反序列化  *  * @param bytes 待反序列化二進制字節數組  * @param  反序列對象類型  * @return 反序列對象  * @throws IOException  * @throws ClassNotFoundException  */ public static <T> T deSerialize(byte[] bytes) throws IOException, ClassNotFoundException { // 字節輸入流
        final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes); // 將二進制字節流反序列化為對象
        final ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream); final T object = (T) objectInputStream.readObject(); // 關閉流
        objectInputStream.close(); byteArrayInputStream.close(); return object; } 

從上面的代碼可以看出,JDK 原生框架使用起來還是有點麻煩的,首先要求對象必須實現java.io.Serializable接口,其次需要借助 IO 流操作來完成序列化和反序列化。與市面上其他開源框架比起來,上面的代碼寫起來非常生硬。

一句話總結:JDK 原生框架易用性稍差。

性能好不好?

(1)序列化體積測試

為了方便測試對比,我定義了一個普通 java 類,后面其他框架的測試基本上也是用這個類:

public class UserDTO implements Serializable { private String name; private String wechatPub; private String job; …… } 

將 UserDTO 類進行實例化

UserDTO userDTO = new UserDTO(); userDTO.setName("雷小帥"); userDTO.setWechatPub("微信公眾號:愛笑的架構師"); userDTO.setJob("優秀碼農"); 

序列化和反序列化測試:

System.out.println("--- 1. jdk 原生測試 ---"); byte[] bytes = JDKSerializationUtil.serialize(userDTO); System.out.println("序列化成功:" + Arrays.toString(bytes)); System.out.println("byte size=" + bytes.length); UserDTO userDTO1 = JDKSerializationUtil.deSerialize(bytes); System.out.println("反序列化成功:" + userDTO1); 

打印出來的結果:

--- 1. jdk 原生測試 --- 序列化成功:[-84, -19, 0, 5, 115, 114, 0, 39, ……
byte size=182 反序列化成功:UserDTO[name='雷小帥', wechatPub='微信公眾號:愛笑的架構師', job='優秀碼農'] 

一個 UserDTO 序列化完之后是 182 個字節,待會對比其他框架就知道,這個水平太差了,Java 原生是自帶的序列化工具,親兒子也不給力啊。

(2)序列化速度測試

接下來我們再測試一下序列化和反序列化的速度,總共循環 100 萬次:

  • JDK 序列化耗時:2314 毫秒
  • JDK 反序列化耗時:4170 毫秒

這個成績怎么樣,后面揭曉。

可擴展強不強?

JDK 原生序列化工具通過在類中定義 serialVersionUID 常量來控制版本:

private static final long serialVersionUID = 7982581299541067770L; 

上面這個serialVersionUID是通過 IDEA 工具自動生成的長整形。其實你也可以不用聲明這個值,JDK 會根據 hash 算法自動生成一個。

如果序列化的時候版本號是當前這個值,反序列化前你將值改變了,那么反序列化的時候就會報錯,提示 ID 不一致。

假如需要在 UserDTO 這個類再加一個字段,那如何支持擴展呢?

你可以改變一下serialVersionUID值就可以了。

框架2:fastjson

是否通用?

fastjson 是阿里巴巴出品的一款序列化框架,可以將對象序列化為 JSON 字符串,類似的框架還有 jackson, gson 等。

由于 JSON 是與語言和平臺無關,因此它的通用性還是很好的。

是否容易使用?

UserDTO 類不需要實現 Serializable 接口,也不需要加 serialVersionUID 版本號,使用起來非常簡單。

將一個對象序列化為 json 字符串:

com.alibaba.fastjson.JSON.toJSONString(obj); 

將 json 字符串反序列化為指定類型:

com.alibaba.fastjson.JSON.parseObject(jsonString, clazz); 

另外 fastjson 框架還提供了很多注解,可以在 UserDTO 類進行配置,實現一些定制化的功能需求。

性能好不好?

(1)序列化體積測試

跟 JDK 原生框架一樣,假設我們已經實例化好了一個UserDTO 對象,分別進行序列化和反序列化測試:

System.out.println("--- 2. fastjson 測試 ---"); String jsonString = FastjsonSerializationUtil.serialize(userDTO); System.out.println("序列化成功: " + jsonString); System.out.println("byte size=" + jsonString.length()); UserDTO userDTO2 = FastjsonSerializationUtil.deSerialize(jsonString, UserDTO.class); System.out.println("反序列化成功:" + userDTO2); 

上面的代碼是將序列化和反序列化代碼封裝到了一個工具類中。運行輸出結果:

--- 2. fastjson 測試 --- 序列化成功: {"job":"優秀碼農","name":"雷小帥","wechatPub":"微信公眾號:愛笑的架構師"} byte size=54 反序列化成功:UserDTO[name='雷小帥', wechatPub='微信公眾號:愛笑的架構師', job='優秀碼農'] 

可以看到序列化之后有 54 個字節,而上面 JDK 原生框架是182 個字節,對比下來發現 fastjson 確實比 JDK 原生框架強了不少,親兒子真不行。

(2)序列化速度測試

序列化體積測試完了之后,我們再測試一下序列化和反序列化速度,經過漫長的等待,循環跑了 100 萬次之后實測結果如下:

  • fastjson 序列化耗時:287 毫秒
  • fastjson 反序列化耗時:365 毫秒

這個結果簡直,人如其名啊,真快~ 你看看隔壁 JDK 原生框架的速度,慘不忍睹,哎……

可擴展強不強?

fastjson 沒有版本控制機制,如果對類進行修改,比如新增熟悉字段,反序列時可以進行配置,忽略不認識的熟悉字段就可以正常進行反序列化。

所以說 fastjson 的擴展性還是很靈活的。

框架3:Kryo

是否通用?

Kryo 是一個快速高效的二進制序列化框架,號稱是 Java 領域最快的。它的特點是序列化速度快、體積小、接口易使用。

Kryo支持自動深/淺拷貝,它是直接通過對象->對象的深度拷貝,而不是對象->字節->對象的過程。

關于 Kryo 更多的介紹可以去 Github 查看:

https://github.com/EsotericSoftware/kryo

關于通用性,Kryo 是一款針對 Java 語言開發的框架,基本很難跨語言使用,因此通用性比較差。

是否容易使用?

先引入 Kryo 依賴:

<dependency> <groupId>com.esotericsoftwaregroupId> <artifactId>kryoartifactId> <version>5.3.0version> dependency> 

Kryo 提供的 API 非常簡潔,Output 類封裝了輸出流操作,使用 writeObject 方法將對象寫入 output 輸出流程即可完成二進制序列化過程。

下面代碼封裝了一個簡單的工具方法:

/**  * 序列化  *  * @param obj  待序列化對象  * @param kryo kryo 對象  * @return 字節數組  */ public static byte[] serialize(Object obj, Kryo kryo) { Output output = new Output(1024); kryo.writeObject(output, obj); output.flush(); return output.toBytes(); } 

Kryo 反序列化也非常簡單,Input 封裝了輸入流操作,通過 readObject 方法從輸入流讀取二進制反序列化成對象。

/**  * 反序列化  *  * @param bytes 待反序列化二進制字節數組  * @param    反序列對象類型  * @return 反序列對象  */ public static <T> T deSerialize(byte[] bytes, Class<T> clazz, Kryo kryo) { Input input = new Input(bytes); return kryo.readObject(input, clazz); } 

另外 Kryo 提供了豐富的配置項,可以在創建 Kryo 對象時進行配置。

總體而言,Kryo 使用起來還是非常簡單的,接口易用性也是非常不錯的。

性能好不好?

(1)序列化體積測試

Kryo 框架與其他框架不同,在實例化的時候可以選擇提前注冊類,這樣序列化反序列化的速度會更快,當然也可以選擇不注冊。

System.out.println("--- 3. kryo 測試 ---"); Kryo kryo = new Kryo(); kryo.setRegistrationRequired(false); // kryo.register(UserDTO.class); byte[] kryoBytes = KryoSerializationUtil.serialize(userDTO, kryo); System.out.println("序列化成功:" + Arrays.toString(kryoBytes)); System.out.println("byte size=" + kryoBytes.length); UserDTO userDTO3 = KryoSerializationUtil.deSerialize(kryoBytes, UserDTO.class, kryo); System.out.println("反序列化成功:" + userDTO3); 

運行結果:

序列化成功:[-123, -28, -68, -104, -25, ……] byte size=60 反序列化成功:UserDTO[name='雷小帥', wechatPub='微信公眾號:愛笑的架構師', job='優秀碼農'] 

從結果來看,序列化后總共是 60 字節。

(2)序列化速度測試

序列化體積測試完了之后,我們再測試一下序列化和反序列化速度,經過漫長的等待,循環跑了 100 萬次之后實測結果如下:

  • kryo 序列化耗時:295 毫秒
  • kryo 反序列化耗時:211 毫秒

這個成績還不錯。

可擴展強不強?

Kryo默認序列化器 FiledSerializer 是不支持字段擴展的,如果想要使用擴展序列化器則需要配置其它默認序列化器。

框架4:Protobuf

是否通用?

Protobuf 是谷歌開源的一款二進制序列化框架。

Protobuf 要求先寫schema描述文件,然后通過編譯器編譯成具體的編程語言(Java、C++、Go 等),因此它是一種語言中立、跨平臺的框架,通用性非常好。

是否容易使用?

先編寫 schema 文件,定義了一個 User 類,擁有三個屬性字段:

syntax = "proto3"; option java_package = "com.example.demo2.serialization.protobuf"; message User { string name = 1; string wechatPub = 2; string job = 3; } 

接著在電腦上安裝好 Protobuf 編譯工具,執行編譯命令:

protoc --java_out=./  user-message.proto 

編譯成功后會生成一個 UserMessage 類。

UserMessage 類包含了很多內容:

首先有一個 Builder 內部類,可以用于實例化對象;

另外還提供了toByteArray(),可以很方便將對象序列化為二進制字節數組;提供了parseFrom()方法可以將對象反序列化為對象。

在接口使用上非常簡單,開箱即用。

性能好不好?

(1)序列化體積測試

使用上面生成的UserMessage類創建一個對象,然后再進行序列化和反序列化測試:

System.out.println("--- 4. protobuf 測試 ---"); UserMessage.User user = UserMessage.User.newBuilder() .setName("雷小帥") .setWechatPub("微信公眾號:愛笑的架構師") .setJob("優秀碼農") .build(); final byte[] protoBufBytes = user.toByteArray(); System.out.println("序列化成功:" + Arrays.toString(protoBufBytes)); System.out.println("byte size=" + protoBufBytes.length); final UserMessage.User user1 = UserMessage.User.parseFrom(protoBufBytes); System.out.println("反序列化成功:" + user1); 

運行結果:

序列化成功:[-123, -28, -68, -104, -25, ……] byte size=63 反序列化成功:UserDTO[name='雷小帥', wechatPub='微信公眾號:愛笑的架構師', job='優秀碼農'] 

序列化后是 63 字節,比 Kryo 稍微多一點點,有點吃驚。

(2)序列化速度測試

序列化體積測試完了之后,我們再測試一下序列化和反序列化速度,經過漫長的等待,循環跑了 100 萬次之后實測結果如下:

  • protobuf 序列化耗時:93 毫秒
  • protobuf 反序列化耗時:341 毫秒

序列化速度很強,但是反序列化為什么慢這么多?

可擴展強不強?

可擴展性是 Protobuf 設計目標之一,我們可以很方便進行字段增刪,新舊協議都可以進行解析。

總結:

本文對常用的框架進行了測試對比,通過觀察 是否通用、是否容易使用、性能好不好、可擴展強不強 這四種維度,我們發現它們各有優劣,大家在進行技術選型時一定要慎重。

最后針對性能測試這一塊,簡單總結一下,給每種框架排個序。

(1)序列化體積

fastjson 54 bytes < Kryo 60 bytes < Protobuf 63 bytes < Java 原生 182 bytes

體積越小,傳輸效率越高,性能更優。Java 親兒子真慘!

(2)序列化速度

protobuf 93 毫秒 < fastjson 289 毫秒 < kryo 295 毫秒 < Java 原生 2247 毫秒

Protobuf 真牛逼,王者!Java 親兒子繼續輸~

(3)反序列化速度

kryo 211 毫秒 < protobuf 341 毫秒 < fastjson 396 毫秒 < Java 原生 4061 毫秒

Kryo 成績比較穩定,序列化和反序列用時接近。Java 親兒子輸麻了!

原文地址:https://mp.weixin.qq.com/s/Z9ahavCHPxQRfBzjuVJwSg

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 蜜桃成品人免费视频 | 中国产一级毛片 | 在线成人免费观看 | 在线视频 日韩 | 男人久久天堂 | 91网站在线观看视频 | 久久久久女人精品毛片 | 99re热视频这里只精品 | 久久九九热re6这里有精品 | 男男羞羞视频网站国产 | 久久亚洲精品国产一区 | 亚洲综人网 | 欧美日韩免费观看视频 | 久久精品视频首页 | 免费啪视频在线观看 | 免费黄色小网站 | 成年人高清视频在线观看 | 欧美视频一区二区 | 国产91精品久久久 | 国产女厕一区二区三区在线视 | 一级片免费在线 | 色骚综合| 香蕉久久久精品 | 久久千人斩 | 国产成人午夜高潮毛片 | 美女扒开胸罩给男生看视频 | 欧产日产国产精品v | 日本不卡一区二区三区在线 | 久久国产夫妻视频 | 高清久久久 | 天堂成人一区二区三区 | 中国杭州少妇xxxx做受 | 国产精品嘿咻嘿咻在线播放 | 国产高潮好爽好大受不了了 | av日韩一区二区三区 | 日本欧美一区二区三区视频麻豆 | 羞羞电影在线观看 | 亚洲电影在线播放 | 久久综合伊人 | 国产一区二区三区四区波多野结衣 | 精品一区二区三区在线播放 |