序列化框架性能對(duì)比

2020年對(duì)我來說是忙碌的一年涛癌,2021年也毫無懸念的需要繼續(xù)忙碌。因?yàn)楣緩?020年從零開始構(gòu)建云原生平臺(tái)斗这,從原有隊(duì)伍中抽調(diào)了一部分人再加上新招聘的組成了目前的團(tuán)隊(duì)承接這項(xiàng)寄托或者影響著公司未來的產(chǎn)品陆错。既然是云原生,微服務(wù)是必不可少的。緩存曲尸、隊(duì)列自然也是必備套件紊册。在redis存儲(chǔ)及隊(duì)列消息發(fā)送時(shí)都面臨序列化框架的選擇比肄。

序列化框架對(duì)于上層應(yīng)用來說是透明的。應(yīng)用的開發(fā)同學(xué)無需關(guān)注或者甚至有些都?jí)焊恢佬蛄谢拇嬖谀叶福鳛榧夹g(shù)中臺(tái)的同學(xué)芳绩,我們還是會(huì)面臨序列化框架選擇的問題。也許你會(huì)嘲笑我撞反,“為什么要糾結(jié)那么一丁點(diǎn)的性能差異妥色,難道Json序列化的可讀性不香嗎?”遏片。但我覺得凡事都應(yīng)該論個(gè)究竟嘹害,如果Json序列化那么香撮竿,為什么還會(huì)有這么多序列化框架的存在? 技術(shù)框架的性能也直接影響到所有應(yīng)用的性能笔呀,所以在選擇框架的時(shí)候除了本身框架的穩(wěn)定性幢踏、維護(hù)性、擴(kuò)展性之外许师,性能表現(xiàn)也會(huì)作為選擇評(píng)比的重要參考指標(biāo)房蝉。

Java生態(tài)的序列化框架有不少。除了Jdk序列化微渠、Jackson搭幻、被安全問題飽受爭議的FastJson之外,像Hessian敛助、Kryo粗卜、Fst、Protostuff等序列化框架也被廣泛應(yīng)用纳击。而針對(duì)這些框架本身的性能表現(xiàn)如何续扔,還是要實(shí)測一把才能有個(gè)準(zhǔn)確的對(duì)比。下面就針對(duì)Kryo焕数、Fst纱昧、Protostuff進(jìn)行下序列化、反序列化的性能測試堡赔。

先看一下要被序列化的對(duì)象识脆。我定義了User、Group對(duì)象善已,應(yīng)該也是我們在構(gòu)建應(yīng)用時(shí)設(shè)計(jì)的最簡單的對(duì)象了灼捂。

@Data

public class User implements Serializable {

????private Stringid;

? ? private String name;

? ? private Integer age;

? ? private String desc;

? ? private Group group;

}

User對(duì)象有個(gè)關(guān)聯(lián)屬性group,表示用戶所屬的分組或者部門换团。

@Data

public class Groupimplements Serializable {

private Stringid;

? ? private Stringname;

? ? private Stringdescription;

}


分別定義了三個(gè)序列化的類悉稠。

Kryo序列化類:

public class JanzKryoSerializerimplements Serializer{

????private Kryokryo;

????private volatile static Serializerserializer;

????private JanzKryoSerializer(){

????????kryo =new Kryo();

????????kryo.register(BigDecimal.class, new DefaultSerializers.BigDecimalSerializer());

????????kryo.register(BigInteger.class, new DefaultSerializers.BigIntegerSerializer());

? ? ? ? kryo.register(Double.class, new DefaultSerializers.DoubleSerializer());

????????kryo.register(Float.class, new DefaultSerializers.FloatSerializer());

????????kryo.register(Byte.class, new DefaultSerializers.ByteSerializer());

? ? ????kryo.register(Integer.class, new DefaultSerializers.IntSerializer());

????????kryo.register(Long.class, new DefaultSerializers.LongSerializer());

? ? ? ? kryo.register(String.class, new DefaultSerializers.StringSerializer());

? ? ? ? kryo.register(StringBuffer.class, new DefaultSerializers.StringBufferSerializer());

? ? ? ? kryo.register(Date.class, new DefaultSerializers.DateSerializer());

? ? ? ? // kryo.setRegistrationRequired(true);

? }

????public static SerializergetInstance(){

????????if(serializer ==null){

????????????synchronized(JanzKryoSerializer.class){

????????????????serializer =new JanzKryoSerializer();

? ? ? ? ???? }

????????}

????????return serializer;

????? }

????@Override

????public byte[]serialize(T obj) {

????????Output output =null;

????????try {

? ? ? ? ? ? ?ByteArrayOutputStream baos =new ByteArrayOutputStream();

? ? ? ? ? ? ? output =new Output(baos);

? ? ? ? ? ? ? kryo.writeClassAndObject(output, obj);

? ? ? ? ? ? ? output.flush();

? ? ? ? ? ? ? return baos.toByteArray();

? ? ? ? ? }catch(Exception e){

? ? ? ? ? ? ? ?e.printStackTrace();

? ? ? ? ? }finally {

????????????????if(output !=null){

????????????????????output.close();

? ? ? ? ? ? ? ? }

????????}

????????return null;

???? }

????@SuppressWarnings("unchecked")

????@Override

????? public T deserialize(byte[] bits, Class clazz) {

????????????if(bits ==null || bits.length ==0){

????????????????return null;

? ? ? ????????}

????????????Input ois =null;

? ? ? ? ? ? try {

????????????????ByteArrayInputStream bais =new ByteArrayInputStream(bits);

? ? ? ? ? ? ? ? ois =new Input(bais);

? ? ? ? ????????? return (T)kryo.readClassAndObject(ois);

? ? ? ? ? ? ?}catch(Exception e){

????????????????e.printStackTrace();

? ? ? ? ? ? ? }finally {

????????????????if(ois !=null){

????????????????????ois.close();

? ? ? ? ? ? ? ? ?}

????????????}

????????????return null;

? }

Fst序列化類:

class JanzFstSerializer implements Serializer{

? ? private FSTConfiguration configuration;

? ? private volatile static Serializer serializer;

? ? private JanzFstSerializer() {

? ? ? ? configuration = FSTConfiguration.createDefaultConfiguration();

? ? }

? ? public static Serializer getInstance() {

? ? ? ? if (serializer == null) {

? ? ? ? ? ? synchronized (JanzFstSerializer.class) {

? ? ? ? ? ? ? ? serializer = new JanzFstSerializer();

? ? ? ? ? ? }

? ? ? ? }

? ? ? ? return serializer;

? ? }

? ? @Override

? ? public byte[] serialize(Object obj) {

? ? ? ? return configuration.asByteArray(obj);

? ? }

? ? @Override

? ? public <T> T deserialize(byte[] bits, Class<T> clazz) {

? ? ? ? if (bits == null) {

? ? ? ? ? ? return null;

? ? ? ? }

? ? ? ? return (T)configuration.asObject(bits);

? ? }

}

Protostuff序列化類:

public class JanzStuffSerializer implements Serializer {

private volatile static Serializer serializer;

private JanzStuffSerializer() {

}

public static Serializer getInstance() {

if (serializer == null) {

synchronized (JanzStuffSerializer.class) {

serializer = new JanzStuffSerializer();

}

}

return serializer;

}

@Override

public <T> byte[] serialize(T obj) {

@SuppressWarnings("unchecked")

Class<T> clazz = (Class<T>) obj.getClass();

? ? ? ? LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);

? ? ? ? try {

? ? ? ? ? ? Schema<T> schema = getSchema(clazz);

? ? ? ? ? ? return ProtostuffIOUtil.toByteArray(obj, schema, buffer);

? ? ? ? } catch (Exception e) {

? ? ? ? ? ? throw new IllegalStateException(e.getMessage(), e);

? ? ? ? } finally {

? ? ? ? ? ? buffer.clear();

? ? ? ? }

}

@Override

public <T> T deserialize(byte[] data, Class<T> clazz) {

try {

? ? ? ? ? ? T obj = clazz.newInstance();

? ? ? ? ? ? Schema<T> schema = getSchema(clazz);

? ? ? ? ? ? ProtostuffIOUtil.mergeFrom(data, obj, schema);

? ? ? ? ? ? return obj;

? ? ? ? } catch (Exception e) {

? ? ? ? ? ? throw new IllegalStateException(e.getMessage(), e);

? ? ? ? }

}

private static Map<Class<?>, Schema<?>> cachedSchema = new ConcurrentHashMap<>();

? ? private static <T> Schema<T> getSchema(Class<T> clazz) {

? ? ? ? @SuppressWarnings("unchecked")

? ? ? ? Schema<T> schema = (Schema<T>) cachedSchema.get(clazz);

? ? ? ? if (schema == null) {

? ? ? ? ? ? schema = RuntimeSchema.getSchema(clazz);

? ? ? ? ? ? if (schema != null) {

? ? ? ? ? ? ? ? cachedSchema.put(clazz, schema);

? ? ? ? ? ? }

? ? ? ? }

? ? ? ? return schema;

? ? }

}

基于定義的三種序列化類,編寫測試方法:

public static void main(String[] args) {

? ? ? ? int count = 100000;

? ? ? ? StopWatch stopWatch = new StopWatch();

? ? ? ? Group group = new Group();

? ? ? ? group.setId("12123123123NBNBNBNBNBNB");

? ? ? ? group.setName("北京致遠(yuǎn)互聯(lián)股份有限公司");

? ? ? ? group.setDescription("XXNB廠商艘包, 坐標(biāo):北京市海淀區(qū)XX中心");

? ? ? ? List<User> userList = new ArrayList<>();

? ? ? ? for (int i = 0; i < count; i++) {

? ? ? ? ? ? User user = new User();

? ? ? ? ? ? user.setId("230028340823048203840823NBNBNBNBNBNB-" + i);

? ? ? ? ? ? user.setAge(i);

? ? ? ? ? ? user.setName("我是來自火星的超人-" + i);

? ? ? ? ? ? user.setGroup(group);

? ? ? ? ? ? user.setDesc("歡迎地球人到火星參觀的猛,順便把我捎回去,我是火星人" + i);

? ? ? ? ? ? userList.add(user);

? ? ? ? }

? ? ? ? JanzKryoSerializer.getInstance();

? ? ? ? JanzFstSerializer.getInstance();

? ? ? ? JanzStuffSerializer.getInstance();

? ? ? ? stopWatch.start("kryo");

? ? ? ? int size = 0;

? ? ? ? for(User user : userList){

? ? ? ? ? ? byte[] bytes = JanzKryoSerializer.getInstance().serialize(user);

? ? ? ? ? ? size += bytes.length;

? ? ? ? ? ? JanzKryoSerializer.getInstance().deserialize(bytes, User.class);

? ? ? ? }

? ? ? ? stopWatch.stop();

? ? ? ? System.out.println("kryo size:" + size);

? ? ? ? size = 0;

? ? ? ? stopWatch.start("fst");

? ? ? ? for(User user : userList){

? ? ? ? ? ? byte[] bytes = JanzFstSerializer.getInstance().serialize(user);

? ? ? ? ? ? size += bytes.length;

? ? ? ? ? ? JanzFstSerializer.getInstance().deserialize(bytes, User.class);

? ? ? ? }

? ? ? ? stopWatch.stop();

? ? ? ? System.out.println("fst size:" + size);

? ? ? ? size = 0;

? ? ? ? stopWatch.start("stuff");

? ? ? ? for(User user : userList){

? ? ? ? ? ? byte[] bytes = JanzStuffSerializer.getInstance().serialize(user);

? ? ? ? ? ? size += bytes.length;

? ? ? ? ? ? JanzStuffSerializer.getInstance().deserialize(bytes, User.class);

? ? ? ? }

? ? ? ? stopWatch.stop();

? ? ? ? System.out.println("stuff size:" + size);

? ? ? ? System.out.println(stopWatch.prettyPrint());

? ? }

基于這樣的測試輸出結(jié)果如下:

kryo size:33058414

fst size:30800878

stuff size:27750158

StopWatch '': running time = 2388100212 ns

---------------------------------------------

ns? ? ? ? %? ? Task name

---------------------------------------------

1097995591? 046%? kryo

826917225? 035%? fst

463187396? 019%? stuff

從測試對(duì)比的結(jié)果來看想虎,protostuff的性能優(yōu)勢還是很明顯的卦尊。在沒有其他條件影響的情況下,我們會(huì)采用protostuff作為默認(rèn)的序列化器舌厨。

在當(dāng)前的Java生態(tài)中岂却,做任何事情都有很多的選擇。本著求真的態(tài)度,我們不妨跑個(gè)demo淌友,做個(gè)簡單壓測煌恢,除了熟悉框架的基本使用之外也跟具體的了解下組件的內(nèi)部實(shí)現(xiàn)原理和實(shí)際表現(xiàn)骇陈。這樣讓我們在做選擇的時(shí)候顯得不會(huì)茫然失措震庭。

實(shí)踐是檢驗(yàn)真理的唯一標(biāo)準(zhǔn)!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末你雌,一起剝皮案震驚了整個(gè)濱河市器联,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌婿崭,老刑警劉巖拨拓,帶你破解...
    沈念sama閱讀 212,454評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異氓栈,居然都是意外死亡渣磷,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門授瘦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來醋界,“玉大人,你說我怎么就攤上這事提完⌒畏模” “怎么了?”我有些...
    開封第一講書人閱讀 157,921評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵徒欣,是天一觀的道長逐样。 經(jīng)常有香客問我,道長打肝,這世上最難降的妖魔是什么脂新? 我笑而不...
    開封第一講書人閱讀 56,648評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮粗梭,結(jié)果婚禮上争便,老公的妹妹穿的比我還像新娘。我一直安慰自己楼吃,他們只是感情好始花,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,770評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著孩锡,像睡著了一般酷宵。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上躬窜,一...
    開封第一講書人閱讀 49,950評(píng)論 1 291
  • 那天浇垦,我揣著相機(jī)與錄音,去河邊找鬼荣挨。 笑死男韧,一個(gè)胖子當(dāng)著我的面吹牛朴摊,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播此虑,決...
    沈念sama閱讀 39,090評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼甚纲,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了朦前?” 一聲冷哼從身側(cè)響起介杆,我...
    開封第一講書人閱讀 37,817評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎韭寸,沒想到半個(gè)月后春哨,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,275評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡恩伺,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,592評(píng)論 2 327
  • 正文 我和宋清朗相戀三年赴背,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片晶渠。...
    茶點(diǎn)故事閱讀 38,724評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡凰荚,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出乱陡,到底是詐尸還是另有隱情浇揩,我是刑警寧澤,帶...
    沈念sama閱讀 34,409評(píng)論 4 333
  • 正文 年R本政府宣布憨颠,位于F島的核電站胳徽,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏爽彤。R本人自食惡果不足惜养盗,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,052評(píng)論 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望适篙。 院中可真熱鬧往核,春花似錦、人聲如沸嚷节。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽硫痰。三九已至衩婚,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間效斑,已是汗流浹背非春。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評(píng)論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人奇昙。 一個(gè)月前我還...
    沈念sama閱讀 46,503評(píng)論 2 361
  • 正文 我出身青樓护侮,卻偏偏與公主長得像,于是被迫代替她去往敵國和親储耐。 傳聞我的和親對(duì)象是個(gè)殘疾皇子羊初,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,627評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容