1.介绍

最近在工作的时候涉及到需要对结构化对象进行序列化的工作,在做了一番调研之后,有很多比JAVA自带的序列化工具要好很多的工具。其中使用最广泛,先在用的比较多的工具就是这个protobuf。

JAVA序列化工具对比:
几种 Java 序列化方案的性能比较

protobuf是由Google开发的一套对数据结构进行序列化的方法,可用做通信协议,数据存储格式,等等。其特点是不限语言、不限平台、扩展性强,就像XML一样。与XML相比,protobuf有以下特点:
操作更简单
序列化后生成的代码体积更小
解析速度更快

缺点:序列化后的是二进制代码(其实也不算缺点,可以通过proto文件来了解)

2.使用

我们这里以protoc-2.4.1为例

1.下载编译工具:protoc-2.4.1-win32.zip
2.配置MAVEN: 在IDEA里面配置pom.xml,添加如下内容:

<dependency>
    <groupId>com.google.protobuf</groupId>
    <artifactId>protobuf-java</artifactId>
    <version>2.4.1</version>
</dependency>

3.编写一个proto文件:

这会生成一个TestMessage类,该类当中包含一个引用的类对象,即Test_net。消息的内容存放在这个引用对象中,即访问目标destination,客户端ID和要获取多少字节的数据(假设有这么个应用场景,需要传递这样的消息)。

MessageProtocol.proto
package com.best.kafka;

option java_package = "com.best.kafka";
option java_outer_classname = "TestMessage";
option optimize_for = SPEED;

message Test_net {
    optional string destination = 1;
    optional string client_id = 2;
    optional int32 fetch_size = 3;
}

这里使用的修饰符定义如下:

  • Required: 表示是一个必须字段,必须相对于发送方,在发送消息之前必须设置该字段的值,对于接收方,必须能够识别该字段的意思。发送之前没有设置required字段或者无法识别required字段都会引发编解码异常,导致消息被丢弃。
  • Optional:表示是一个可选字段,可选对于发送方,在发送消息时,可以有选择性的设置或者不设置该字段的值。对于接收方,如果能够识别可选字段就进行相应的处理,如果无法识别,则忽略该字段,消息中的其它字段正常处理。---因为optional字段的特性,很多接口在升级版本中都把后来添加的字段都统一的设置为optional字段,这样老的版本无需升级程序也可以正常的与新的软件进行通信,只不过新的字段无法识别而已,因为并不是每个节点都需要新的功能,因此可以做到按需升级和平滑过渡。
  • Repeated:表示该字段可以包含0~N个元素。其特性和optional一样,但是每一次可以包含多个值。可以看作是在传递一个数组的值。

Protobuf定义了一套基本数据类型。几乎都可以映射到C++\Java等语言的基础数据类型.

4.使用编译工具自动生成TestMessage类

执行后产生的TestMessage.java

5.序列化和反序列化

直接调用相关接口即可完成序列化和反序列化的操作
TestSerialize

package com.best.kafka;

import java.util.Arrays;

/**
 * @author Wan Kaiming on 2016/6/2
 * @version 1.0
 */
public class TestSerialize {

    public static void main(String[] args){
        //--------------------序列化----------------------------
        //获取Builder对象
        TestMessage.Test_net.Builder builder = TestMessage.Test_net.newBuilder();

        //做一些类成员变量值的设定
        builder.setDestination("example");
        builder.setClientId("1001");
        builder.setFetchSize(100);

        //创建TestMessage中的Test_net消息对象
        TestMessage.Test_net test_net= builder.build();

        //序列化成字节数组
        byte[] buffer = test_net.toByteArray();

        //打印下字节数组内容
        System.out.println("序列化后的字节内容为:");
        System.out.println(Arrays.toString(buffer));

        //------------------------------反序列化---------------------------
        try{
            //创建一个新的Test_net消息对象用于接受反序列化后的结果
            TestMessage.Test_net test_net_Deserialize = TestMessage.Test_net.parseFrom(buffer);

            //打印序列化后的结果
            System.out.println("序列化后的消息信息为");
            String destination = test_net_Deserialize.getDestination();
            String clientID = test_net_Deserialize.getClientId();
            int fetch_size = test_net_Deserialize.getFetchSize();
            System.out.println("destination: " + destination );
            System.out.println("clientID: " + clientID);
            System.out.println("fetch_size: "+fetch_size);
        }
        catch(Exception ex){
            System.out.println(ex.getMessage());
        }





    }
}

运行结果:

3.总结

protobuf用起来十分方便。值得注意的是,你需要序列化的对象必须成员变量都是基本类型。如果你要序列化的是引用类型,则需要自己手动做一些操作,使得其支持protobuf传输。

如果需要嵌套其他proto定义的消息,可以直接在proto文件当中使用import 来导入使用,import 后面记得使用引号(protobuf是C++实现的,所以这个proto写法上还是符合C++风格的)