蹲厕所的熊

benjaminwhx

分布式服务序列化框架的使用与选型

2018-06-12 作者: 吴海旭


  1. 1、序列化原理
  2. 2、序列化框架的使用
    1. Java默认的序列化
    2. XML序列化框架
      1. XStream
      2. XMLEncoder和XMLDecoder
    3. JSON序列化框架
    4. Hessian序列化框架
    5. protobuf序列化框架
    6. protostuff序列化框架
    7. Avro序列化框架
    8. JBoss Marshalling序列化框架
  3. 3、序列化框架的选型
  4. 4、测试数据对比

1、序列化原理

序列化(Serialization)是将对象的状态信息转换为可存储或传出的形式过程。简言之,把对象转换为字节序列的过程称为对象的序列化。

而反序列化(Deserialization)是序列化的逆过程。将字节数组反序列化为对象,把字节序列恢复为对象的过程称为对象的反序列化。

序列化能帮助我们解决如下问题:

  • 通过将对象序列化为字节数组,使得不共享内存但通过网络连接的系统之间能够进行对象的传输。
  • 通过将对象序列化为字节数组,能够将对象永久存储到存储设备。
  • 解决远程接口调用JVM之间内存无法共享的问题。

评价一个序列化算法优劣的两个重要指标。

  • 序列化后码流的大小。
  • 序列化本身的速度及系统资源开销大小(包括内存、CPU等)

在分布式服务中,我们经常会进行RPC调用,而序列化框架则是RPC框架衡量性能的重要指标。下面我会通过市面上不同类型的序列化框架的使用来让大家认识到不同框架之间的区别,最后进行选型分析。

首先定义一个序列化/反序列化的通用接口以及一个对象Person:

public interface ISerializer {
    public <T> byte[] serialize(T obj);
    public <T> T deserialize(byte[] data, Class<T> clazz);
}

@Data
public class Person {
    private int age;
    private String name;

    public Person(int age, String name) {
        this.age = age;
        this.name = name;
    }
}

2、序列化框架的使用

Java默认的序列化

public class DefaultJavaSerializer implements ISerializer {

    @Override
    public <T> byte[] serialize(T obj) {
        if (obj == null) {
            return null;
        }
        ByteArrayOutputStream bos = null;
        ObjectOutputStream oos = null;
        try {
            bos = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(bos);
            oos.writeObject(obj);
            return bos.toByteArray();
        } catch (IOException e) {
            throw new SerializationException("Non-serializable object.", e);
        } finally {
            IOUtils.closeIO(oos, bos);
        }
    }

    @Override
    public <T> T deserialize(byte[] data, Class<T> clazz) {
        if (data == null) {
            return null;
        }
        ByteArrayInputStream bis = null;
        ObjectInputStream ois = null;
        try {
            bis = new ByteArrayInputStream(data);
            ois = new ObjectInputStream(bis);
            return (T) ois.readObject();
        } catch (Exception e) {
            throw new IllegalArgumentException("Can't-deserialize object", e);
        } finally {
            IOUtils.closeIO(bis, ois);
        }
    }
}

这种Java自带的序列化方式在之前的文章中已经提到过了,前提是Person类必须得实现 Serializable 接口。加上之后能够正常输出。

Java默认的序列化机制优缺点都非常明显。

优点:

  • Java语言自带,无须额外引入第三方依赖。
  • 与Java语言有天然的最好的易用性与亲和性。

缺点:

  • 只支持Java语言,不支持跨语言。
  • Java默认序列化性能欠佳,序列化后产生的码流过大,对于引用过深的对象序列化易发生栈溢出。

XML序列化框架

XML序列化的优势在于可读性好,利于调试。因为使用标签来表示数据会使得码流变大,因此效率不高。适用于对性能要求不高,且QPS较低的企业级内部系统之间的数据交换的场景,又因XML具有语言无关性,可用于异构系统之间的数据交换协议。其中我们熟悉的WebService相关的协议就是采用XML格式对数据进行序列化的。

XML序列化又多种实现方式。这里介绍XStream与Java自带的XML序列化两种方式。

XStream

使用XStream之前首先引入jar包:

<dependency>
    <groupId>com.thoughtworks.xstream</groupId>
    <artifactId>xstream</artifactId>
    <version>1.4.10</version>
</dependency>

序列化和反序列化直接对XStream类的相关方法操作即可。

public class XmlSerializer implements ISerializer {

    private static final XStream xStream = new XStream(new DomDriver());

    @Override
    public <T> byte[] serialize(T obj) {
        return xStream.toXML(obj).getBytes();
    }

    @Override
    public <T> T deserialize(byte[] data, Class<T> clazz) {
        String xml = new String(data);
        return (T) xStream.fromXML(xml);
    }
}

XMLEncoder和XMLDecoder

Java自带方式实现XML序列化/反序列化主要是用 java.beans.XMLEncoderjava.beans.XMLDecoder 类完成相应的功能。

public class Xml2Serializer implements ISerializer {

    @Override
    public <T> byte[] serialize(T obj) {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        XMLEncoder xe = new XMLEncoder(bos, "utf-8", true, 0);
        xe.writeObject(obj);
        xe.close();
        return bos.toByteArray();
    }

    @Override
    public <T> T deserialize(byte[] data, Class<T> clazz) {
        XMLDecoder xd = new XMLDecoder(new ByteArrayInputStream(data));
        Object obj = xd.readObject();
        xd.close();
        return (T) obj;
    }
}

JSON序列化框架

JSON是一种轻量级的数据交换格式。相比XML,JSON码流更小,而且保留了XML可读性好的优势。

JSON序列化常用的开源工具有以下几个:

  • Jackson
  • fastjson
  • GSON

相比较而言,Jackson与fastjson比GSON的性能要好。Jackson、GSON相对fastjson稳定性更好,fastjson对序列化对象本身有一些额外的要求,比如序列化对象的属性必须实现set/get方法才能完成对该属性的序列化。fastjson的优势在于非常已用的API操作及高性能。所以这里我们就fastjson来实现以下序列化/反序列化操作。

public class FastJsonSerializer implements ISerializer {

    @Override
    public <T> byte[] serialize(T obj) {
        JSON.DEFFAULT_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
        return JSON.toJSONString(obj, SerializerFeature.WriteDateUseDateFormat).getBytes();
    }

    @Override
    public <T> T deserialize(byte[] data, Class<T> clazz) {
        return JSON.parseObject(new String(data), clazz);
    }
}

依赖配置如下:

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.29</version>
</dependency>

Hessian序列化框架

Hessian是一个支持跨语言传输的二进制序列化协议。相对于Java默认的序列化机制,Hessian具有更好的性能与易用性,而且支持多种不同的语言。

public class HessianSerializer implements ISerializer {

    @Override
    public <T> byte[] serialize(T obj) {
        if (obj == null) {
            throw new NullPointerException();
        }

        try {
            ByteArrayOutputStream os = new ByteArrayOutputStream();
            HessianOutput ho = new HessianOutput(os);
            ho.writeObject(obj);
            return os.toByteArray();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public <T> T deserialize(byte[] data, Class<T> clazz) {
        if (data == null) {
            throw new NullPointerException();
        }

        try {
            ByteArrayInputStream is = new ByteArrayInputStream(data);
            HessianInput hi = new HessianInput(is);
            return (T) hi.readObject();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

依赖配置如下:

<dependency>
    <groupId>com.caucho</groupId>
    <artifactId>hessian</artifactId>
    <version>4.0.38</version>
</dependency>

protobuf序列化框架

protobuf是Google的一种数据交换的格式,它独立于语言,独立于平台。Google提供了多种语言的实现:Java、C#、C++、Go和Python,每一种实现都包含了相应语言的编译器及库文件。protobuf是一个纯粹的展示层协议,可以和各种传输层协议一起使用。protobuf的文档也非常完善。

protobuf具有广泛的用户基础,空间开销小及高解析性能是其亮点,非常适合于公司内部对性能要求高的RPC调用。由于其解析性能高,序列化后数据量相对少,也适合应用层对象的持久化场景。

但是它的主要问题在于需要编写.proto IDL文件,使用起来工作量稍大,且需要额外学习proto IDL特有的语法,增加了额外的学习成本。

使用protobuf的一般步骤如下。

  1. 配置开发环境,安装Protocol Compiler代码编译器。
  2. 编写.proto文件,定义序列化对象的数据结构。
  3. 基于编写的.proto文件,使用Protocol Compiler编译生成对应的序列化/反序列化工具类。
  4. 基于自动生成的代码,编写自己的序列化应用。

下面将按照上述4个步骤,以Java为例提供一个完整的例子,演示如何使用protobuf协议进行数据序列化/反序列化操作。

第一步:以Mac系统为例,首先在 https://github.com/google/protobuf/releases 中选择一个版本进行下载,这里我下载的是 protobuf-java-3.5.0.tar.gz ,解压并把bin目录下的可执行文件 protoc 拷贝到项目的 src/main/protobuf 目录中。

接着引入protobuf相关的jar包:

<dependency>
    <groupId>com.google.protobuf</groupId>
    <artifactId>protobuf-java</artifactId>
    <version>3.5.0</version>
</dependency>
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.6</version>
</dependency>

第二步:在当前目录下编写proto文件,这里我编写的文件为 addressbook.proto

syntax = "proto2";

package com.github.bean.protobuf;

option java_package = "com.github.bean.protobuf";
option java_outer_classname = "AddressBookProtos";

message Person {
  required string name = 1;
  required int32 id = 2;
  optional string email = 3;

  enum PhoneType {
    MOBILE = 0;
    HOME = 1;
    WORK = 2;
  }

  message PhoneNumber {
    required string number = 1;
    optional PhoneType type = 2 [default = HOME];
  }

  repeated PhoneNumber phones = 4;
}

message AddressBook {
  repeated Person people = 1;
}

第三步:执行如下命令,生成相关的序列化工具类。

projectDir/src/main/protobuf/protoc --java_out=projectDir/src/main/java/ projectDir/src/main/protobuf/addressbook.proto

执行成功后,会在当前目录下的 com/github/serializer/protobuf 包下生成java文件 AddressBookProtos.java

第四步:编写应用的序列化和反序列化方法。

AddressBookProtos.Person person = AddressBookProtos.Person.newBuilder()
        .setEmail("benjaminwhx@sina.com")
        .setId(10000)
        .setName("benjamin")
        .addPhones(AddressBookProtos.Person.PhoneNumber.newBuilder()
                .setNumber("18888888888")
                .setType(AddressBookProtos.Person.PhoneType.HOME)
                .build()).build();

// 序列化方式1
System.out.println(person.toByteString());
// 序列化方式2
System.out.println(Arrays.toString(person.toByteArray()));

// 反序列化方式1
AddressBookProtos.Person newPerson = AddressBookProtos.Person.parseFrom(person.toByteString());
System.out.println("反序列化1:" + newPerson);
// 反序列化方式2
newPerson = AddressBookProtos.Person.parseFrom(person.toByteArray());
System.out.println("反序列化2:" + newPerson);

在以上的代码基础上,我们可以利用Java反射抽取一个protobuf对象序列化/反序列化的通用方法。

public class ProtobufSerializer implements ISerializer {

    @Override
    public <T> byte[] serialize(T obj) {
        try {
            if (!(obj instanceof GeneratedMessageV3)) {
                throw new UnsupportedOperationException("not supported obj type");
            }
            return (byte[]) MethodUtils.invokeMethod(obj, "toByteArray");
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public <T> T deserialize(byte[] data, Class<T> clazz) {
        try {
            if (!GeneratedMessageV3.class.isAssignableFrom(clazz)) {
                throw new UnsupportedOperationException("not supported obj type");
            }
            Object o = MethodUtils.invokeStaticMethod(clazz, "getDefaultInstance");
            return (T) MethodUtils.invokeMethod(o, "parseFrom", new Object[]{data});
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

protostuff序列化框架

刚才学习过protobuf序列化框架的同学肯定体验到了“复杂”的感觉,但是它为了兼容各种语言的代码只能定义自己的IDL文件,但是由于Java具有反射和动态代码生成的能力,这个预编译过程并不是必须的,完全可以在代码执行的时候去实现。protostuff就实现了该功能。protostuff基于Google protobuf,其中,protostuff-runtime实现了无须预编译对Java Bean进行protobuf序列化/反序列化的能力。

对于仅使用Java语言,且无须跨语言的使用场景,protostuff继承了Google protobuf的高性能的同时免去了编写.proto文件的麻烦,是非常值得推荐的序列化/反序列化方案。

首先引入对应的Maven依赖配置:

<dependency>
    <groupId>io.protostuff</groupId>
    <artifactId>protostuff-runtime</artifactId>
    <version>1.6.0</version>
</dependency>
<dependency>
    <groupId>io.protostuff</groupId>
    <artifactId>protostuff-core</artifactId>
    <version>1.6.0</version>
</dependency>

对应的代码如下:

public class ProtostuffSerializer implements ISerializer {

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

    private <T> Schema<T> getSchema(Class<T> clz) {
        Schema<T> schema = (Schema<T>) cachedSchema.get(clz);
        if (schema == null) {
            schema = RuntimeSchema.createFrom(clz);
            cachedSchema.put(clz, schema);
        }
        return schema;
    }

    @Override
    public <T> byte[] serialize(T obj) {
        Class<T> clz = (Class<T>) obj.getClass();
        Schema<T> schema = getSchema(clz);
        return ProtostuffIOUtil.toByteArray(obj, schema, LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE));
    }

    @Override
    public <T> T deserialize(byte[] data, Class<T> clazz) {
        // 实例化clazz类的对象
        T obj = objenesis.newInstance(clazz);
        ProtostuffIOUtil.mergeFrom(data, obj, getSchema(clazz));
        return obj;
    }
}

Avro序列化框架

Avro是一个数据序列化的项目,最开始是Hadoop的子项目之一,最后加入Apache称为独立的开源项目。Avro提供的功能与protobuf类似,但是不同之处有以下几点:

  • 动态类型:Avro无须生成代码。数据总是伴以模式定义,这样就可以在不生成代码、静态数据类型的情况下对数据进行所有处理,有利于构建通用的数据处理系统和语言。
  • 无标记数据:由于在读取数据时有模式定义,这就大大减少了数据编辑所需的类型信息,从而减少序列化空间开销。
  • 不用手动分配的字段ID:当数据模式发生变化,处理数据时总是同时提供新旧模式,差异就可以用字段名来做符号化的分析。

由于性能高,基本代码少和产出数据量精简等特点,很多知名开源项目都应用了Avro,包括Hadoop、Cassandra等。

首先引入对应的Maven依赖配置:

<dependency>
    <groupId>org.apache.avro</groupId>
    <artifactId>avro</artifactId>
    <version>1.8.2</version>
</dependency>

当然如果你想生成对应的java文件,还得加入对应的maven plugin:

<plugin>
    <groupId>org.apache.avro</groupId>
    <artifactId>avro-maven-plugin</artifactId>
    <version>1.8.1</version>
    <executions>
        <execution>
            <phase>generate-sources</phase>
            <goals>
                <goal>schema</goal>
            </goals>
            <configuration>
                <sourceDirectory>${project.basedir}/src/main/avro/</sourceDirectory>
                <outputDirectory>${project.basedir}/src/main/java/</outputDirectory>
            </configuration>
        </execution>
    </executions>
</plugin>

和protobuf一样,Avro首先得使用IDL定义一个Schema文件 user.avsc(放在maven plugin定义的sourceDirectory下):

{"namespace": "com.github.bean.avro",
 "type": "record",
 "name": "User",
 "fields": [
     {"name": "name", "type": "string"},
     {"name": "age",  "type": ["int", "null"]},
     {"name": "email", "type": ["string", "null"]}
 ]
}

接下来执行 mvn clean install 就会在 outputDirectory 下生成对应的 User.java 文件,接着我们对它进行序列化和反序列化操作。

User userAvro = new User();
userAvro.setAge(26);
userAvro.setEmail("benjaminwhx@sina.com");
userAvro.setName("benjamin");
// 1、序列化
DatumWriter<User> writer = new SpecificDatumWriter<>(User.class);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
BinaryEncoder binaryEncoder = EncoderFactory.get().directBinaryEncoder(bos, null);
writer.write(userAvro, binaryEncoder);
byte[] data = bos.toByteArray();

// 2、反序列化
DatumReader<User> reader = new SpecificDatumReader<>(User.class);
ByteArrayInputStream bis = new ByteArrayInputStream(data);
BinaryDecoder binaryDecoder = DecoderFactory.get().directBinaryDecoder(bis, null);
User deUser = reader.read(userAvro, binaryDecoder);
System.out.println(deUser);

当然我们也可以直接使用schema文件进行序列化与反序列化操作:

Schema schema = new Schema.Parser().parse(new File("/Users/Benjamin/idea_project/local/src/main/avro/user.avsc"));
GenericRecord user1 = new GenericData.Record(schema);
user1.put("age", 18);
user1.put("name", "benjamin");
user1.put("email", "benjaminwhx@sina.com");
// 1、序列化
DatumWriter<GenericRecord> datumWriter = new GenericDatumWriter<GenericRecord>(schema);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
BinaryEncoder binaryEncoder = EncoderFactory.get().directBinaryEncoder(bos, null);
datumWriter.write(user1, binaryEncoder);
byte[] bytes = bos.toByteArray();

// 2、反序列化
DatumReader<GenericRecord> datumReader = new GenericDatumReader<GenericRecord>(schema);
BinaryDecoder binaryDecoder = DecoderFactory.get().directBinaryDecoder(new ByteArrayInputStream(bytes), null);
GenericRecord read = datumReader.read(new GenericData.Record(schema), binaryDecoder);
System.out.println(read);

利用以上方法,我们可以抽取成一个公共的序列化/反序列化类:

public class AvroSerializer implements ISerializer {

    private Objenesis objenesis = new ObjenesisStd();

    @Override
    public <T> byte[] serialize(T obj) {
        try {
            if (!(obj instanceof SpecificRecordBase)) {
                throw new UnsupportedOperationException("not supported obj type");
            }
            DatumWriter writer = new SpecificDatumWriter(obj.getClass());
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            BinaryEncoder binaryEncoder = EncoderFactory.get().directBinaryEncoder(bos, null);
            writer.write(obj, binaryEncoder);
            return bos.toByteArray();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public <T> T deserialize(byte[] data, Class<T> clazz) {
        try {
            if (!SpecificRecordBase.class.isAssignableFrom(clazz)) {
                throw new UnsupportedOperationException("not supported clazz type");
            }
            DatumReader reader = new SpecificDatumReader(clazz);
            ByteArrayInputStream bis = new ByteArrayInputStream(data);
            BinaryDecoder binaryDecoder = DecoderFactory.get().directBinaryDecoder(bis, null);
            return (T) reader.read(objenesis.newInstance(clazz), binaryDecoder);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

JBoss Marshalling序列化框架

JBoss Marshalling是一个Java对象序列化包,兼容Java原生的序列化机制,对Java原生序列化机制做了优化,使其在性能上有很大提升。在保持跟 java.io.Serializable 接口兼容的同时增加了一些可调的参数和附加特性,这些参数和附加的特性,可通过工厂类进行配置,对原生Java序列化时一个很好的替代。

首先引入对应的Maven依赖配置:

<dependency>
    <groupId>org.jboss.marshalling</groupId>
    <artifactId>jboss-marshalling-serial</artifactId>
    <version>2.0.5.Final</version>
</dependency>

JBoss Marshalling序列化/反序列化示例如下:

public class MarshallingSerializer implements ISerializer {
    final static MarshallingConfiguration CONFIGURATION = new MarshallingConfiguration();
    // 获得序列化工厂对象,参数serial是标识创建的是Java序列化工厂对象
    final static MarshallerFactory MARSHALLER_FACTORY = Marshalling.getProvidedMarshallerFactory("serial");

    static {
        CONFIGURATION.setVersion(5);
    }

    @Override
    public <T> byte[] serialize(T obj) {
        final ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try {
            Marshaller marshaller = MARSHALLER_FACTORY.createMarshaller(CONFIGURATION);
            marshaller.start(Marshalling.createByteOutput(bos));
            marshaller.writeObject(obj);
            marshaller.finish();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return bos.toByteArray();
    }

    @Override
    public <T> T deserialize(byte[] data, Class<T> clazz) {
        try {
            ByteArrayInputStream bis = new ByteArrayInputStream(data);
            Unmarshaller unmarshaller = MARSHALLER_FACTORY.createUnmarshaller(CONFIGURATION);
            unmarshaller.start(Marshalling.createByteInput(bis));
            Object o = unmarshaller.readObject();
            unmarshaller.finish();
            return (T) o;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

使用JBoss Marshalling进行序列化操作时,必须要保证序列化对象实现了Serializable接口。

3、序列化框架的选型

前面我们介绍了几种最常用的序列化框架的使用。每一种序列化协议都有自己的优缺点及适用场景。我们在选型的时候,一般会从下面几个方面来考察。

1、技术层面的考虑

  • 序列化空间开销,即序列化结果产生的码流大小,码流过大会对带宽、存储空间造成较大压力。
  • 序列化时间开销,即序列化过程消耗的时长,序列化消耗时间过长会拖慢整个服务的响应时间。
  • 序列化协议是否支持跨平台、跨语言,公司内部存在异构系统通信需求时,往往要求RPC框架采用的序列化协议支持跨平台、跨语言。
  • 可扩展性/兼容性,在实际业务开发过程中,我们的系统往往需要随着需求的快速迭代快速更新,这就要求我们采用的序列化协议具有良好的可扩展性/兼容性,比如,在现有的序列化数据结构中新增某个业务字段,而不会影响到现有的业务服务。
  • 成熟度及支持的数据结构的丰富性也是一个需要重点考量的方面。

2、其他层面的考虑

  • 技术的流行程度,背后是否有大公司技术支撑,是否是一个长期发展的持续进化的技术,是否已经得到业界的充分验证。
  • 学习难度和易用性。

3、选型建议

  • 对于公司间的系统调用,性能要求在100ms以上的服务,基于XML的SOAP协议是一个值得考虑的方案。
  • 基于Web Browser的Ajax,以及Mobile App与服务端之间的通信,JSON协议是首选。对于性能要求不太高,或者以动态类型语言为主,或者传输数据载荷很小的运用场景,JSON也是一个非常不错的选择。
  • 对于调试环境比较恶劣的场景,采用JSON或者XML能够极大地提高调试效率,降低系统开发成本。
  • 对性能和简洁性有极高要求的场景,Hessian、protobuf、Avro之间具有一定的竞争关系。其中Hessian是在性能和稳定性同时考虑下最优的序列化协议。
  • 对于T级别的数据的持久化应用场景,protobuf和Avro是首要选择。如果持久化后的数据存储在Hadoop子项目里,Avro会是更好的选择。
  • 由于Avro的设计理念偏向于动态类型语言,对于以动态语言为主的应用场景,Avro是更好的选择。
  • 对于持久层非Hadoop项目,以静态类型语言为主的应用场景,protobuf会更符合静态类型语言工程师的开发习惯。
  • 对序列化之后需要支持不同的传输层协议,或者需要跨防火墙访问的高性能场景,Protobuf可以优先考虑。

4、测试数据对比

xx曾经说过:一切没有数据支撑的选型建议都是耍流氓。

基于此,我使用JMH做了详尽的基准测试,测试代码地址:https://github.com/benjaminwhx/p_rpc/blob/master/serialize/src/main/java/com/github/BenchmarkTest.java

因测试的时候发现JDK的xml序列化/反序列化性能极差,故这里不放出来讨论)

serialize1

serialize2

通过对比图可以看出无论是性能还是产生的码流大小,protobuf/protostuff都非常优秀,而如果要使用JSON进行传输,FastJson绝对是首选。而从各方面来看,Hessian也不错。



坚持原创技术分享,您的支持将鼓励我继续创作!



分享

评论