序列化框架性能对比

2020年对我来说是忙碌的一年,2021年也毫无悬念的需要继续忙碌。因为公司从2020年从零开始构建云原生平台,从原有队伍中抽调了一部分人再加上新招聘的组成了目前的团队承接这项寄托或者影响着公司未来的产品。既然是云原生,微服务是必不可少的。缓存、队列自然也是必备套件。在redis存储及队列消息发送时都面临序列化框架的选择。

序列化框架对于上层应用来说是透明的。应用的开发同学无需关注或者甚至有些都压根不知道序列化的存在,但作为技术中台的同学,我们还是会面临序列化框架选择的问题。也许你会嘲笑我,“为什么要纠结那么一丁点的性能差异,难道Json序列化的可读性不香吗?”。但我觉得凡事都应该论个究竟,如果Json序列化那么香,为什么还会有这么多序列化框架的存在? 技术框架的性能也直接影响到所有应用的性能,所以在选择框架的时候除了本身框架的稳定性、维护性、扩展性之外,性能表现也会作为选择评比的重要参考指标。

Java生态的序列化框架有不少。除了Jdk序列化、Jackson、被安全问题饱受争议的FastJson之外,像Hessian、Kryo、Fst、Protostuff等序列化框架也被广泛应用。而针对这些框架本身的性能表现如何,还是要实测一把才能有个准确的对比。下面就针对Kryo、Fst、Protostuff进行下序列化、反序列化的性能测试。

先看一下要被序列化的对象。我定义了User、Group对象,应该也是我们在构建应用时设计的最简单的对象了。

@Data

public class User implements Serializable {

    private Stringid;

    private String name;

    private Integer age;

    private String desc;

    private Group group;

}

User对象有个关联属性group,表示用户所属的分组或者部门。

@Data

public class Groupimplements Serializable {

private Stringid;

    private Stringname;

    private Stringdescription;

}


分别定义了三个序列化的类。

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("北京致远互联股份有限公司");

        group.setDescription("XXNB厂商, 坐标:北京市海淀区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());

    }

基于这样的测试输出结果如下:

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

从测试对比的结果来看,protostuff的性能优势还是很明显的。在没有其他条件影响的情况下,我们会采用protostuff作为默认的序列化器。

在当前的Java生态中,做任何事情都有很多的选择。本着求真的态度,我们不妨跑个demo,做个简单压测,除了熟悉框架的基本使用之外也跟具体的了解下组件的内部实现原理和实际表现。这样让我们在做选择的时候显得不会茫然失措。

实践是检验真理的唯一标准!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容