一看就會系列之序列化(Java/avro/protobuf/thrift)入門

一、Java原生序列化

二、avro序列化

三、protobuf序列化

四、thrift序列化

一、Java原生序列化
實體類 :

Employee.java

package com.zhuyun.serialize.java;
 
import java.io.Serializable;
 
public class Employee implements Serializable
{
   public String name;
   public String address;
   public transient int SSN;
   public int number;
   public void mailCheck()
   {
      System.out.println("Mailing a check to " + name
                           + " " + address);
   }
}
 

序列化,並存儲在文件中:

SerializeDemo.java

package com.zhuyun.serialize.java;
 
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
 
 
//序列化
public class SerializeDemo
{
   public static void main(String [] args)
   {
      Employee employee = new Employee();
      employee.name = "Reyan Ali";
      employee.address = "Phokka Kuan, Ambehta Peer";
      employee.SSN = 11122333;
      employee.number = 101;
      try
      {
         FileOutputStream fileOut =
         new FileOutputStream("employee.ser");
         ObjectOutputStream out = new ObjectOutputStream(fileOut);
         out.writeObject(employee);
         out.close();
         fileOut.close();
         System.out.printf("Serialized data is saved in employee.ser");
      }catch(IOException i)
      {
          i.printStackTrace();
      }
   }
}
如圖,會生成一個文件,保存着實體類Employee序列化後的二進制內容

 

從文件中反序列化成Employee實體類:

DeserializeDemo.java

package com.zhuyun.serialize.java;
 
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
 
public class DeserializeDemo
{
   public static void main(String [] args)
   {
      Employee employee = null;
      try
      {
         FileInputStream fileIn = new FileInputStream("employee.ser");
         ObjectInputStream in = new ObjectInputStream(fileIn);
         employee = (Employee) in.readObject();
         in.close();
         fileIn.close();
      }catch(IOException i)
      {
         i.printStackTrace();
         return;
      }catch(ClassNotFoundException c)
      {
         System.out.println("Employee class not found");
         c.printStackTrace();
         return;
      }
      System.out.println("Deserialized Employee...");
      System.out.println("Name: " + employee.name);
      System.out.println("Address: " + employee.address);
      System.out.println("SSN: " + employee.SSN);
      System.out.println("Number: " + employee.number);
    }
}
運行結果如下:

一般來說,Java的序列化性能比較低,在一些通信的開源框架中不會使用Java原生的序列化。

 

二、avro序列化
pom依賴

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

定義一個user schema,格式是avro文件

user.avsc

{"namespace": "com.zhuyun.serialize.java.avro",
 "type": "record",
 "name": "User",
 "fields": [
     {"name": "name", "type": "string"},
     {"name": "favorite_number",  "type": ["int", "null"]},
     {"name": "favorite_color", "type": ["string", "null"]}
 ]
}
下載avro的工具:

http://www.apache.org/dyn/closer.cgi/avro/

 

使用下載的avro工具,根據user.avsc文件來生成一個user.java的文件:

java -jar /path/to/avro-tools-1.9.0.jar compile schema user.avsc .
user.java文件類似如下(注:不要手動修改該類):

 

序列化和反序列化:

AvroDemo.java

package com.zhuyun.serialize.java.avro;
 
import java.io.File;
import java.io.IOException;
 
import org.apache.avro.file.DataFileReader;
import org.apache.avro.file.DataFileWriter;
import org.apache.avro.io.DatumReader;
import org.apache.avro.io.DatumWriter;
import org.apache.avro.specific.SpecificDatumReader;
import org.apache.avro.specific.SpecificDatumWriter;
 
public class AvroDemo {
    
    public static void main(String[] args) {
        //創建用戶
        User user1 = new User();
        user1.setName("Alyssa");
        user1.setFavoriteNumber(256);
        // Leave favorite color null
 
        // Alternate constructor
        User user2 = new User("Ben", 7, "red");
 
        // Construct via builder
        User user3 = User.newBuilder()
                     .setName("Charlie")
                     .setFavoriteColor("blue")
                     .setFavoriteNumber(null)
                     .build();
        
        
        System.out.println("user1=" + user1);
        System.out.println("user2=" + user2);
        System.out.println("user3=" + user3);
        
        //序列化並存儲到磁盤文件
        try {
            DatumWriter<User> userDatumWriter = new SpecificDatumWriter<User>(User.class);
            DataFileWriter<User> dataFileWriter = new DataFileWriter<User>(userDatumWriter);
            dataFileWriter.create(user1.getSchema(), new File("users.avro"));
            dataFileWriter.append(user1);
            dataFileWriter.append(user2);
            dataFileWriter.append(user3);
            dataFileWriter.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        
        //從磁盤文件中反序列化
        try {
            DatumReader<User> userDatumReader = new SpecificDatumReader<User>(User.class);
            DataFileReader<User> dataFileReader = new DataFileReader<User>(new File("users.avro"), userDatumReader);
            User user = null;
            while (dataFileReader.hasNext()) {
            user = dataFileReader.next(user);
            System.out.println("user=" + user);
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        
    }
    
}
初始化實體類的方式有好幾種,例如setter、構造函數、builder模式等。

序列化後會生成一個文件,保存着實體類user序列化後的二進制內容

執行結果如下:

 

三、protobuf序列化
pom依賴:

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

定義一個Student schema,文件格式是.proto,內容如下:

Student.proto

syntax = "proto3";
 
message Student
{
    int32 number = 1;
    string name = 2;
    int32 sex = 3;
    string hobby = 4;
    string skill = 5;
}
 

下載protobuf的工具:

https://github.com/protocolbuffers/protobuf/releases/

 

使用下載的protobuf工具,根據Student.proto文件來生成一個java文件:

protoc -I=proto的輸入目錄 --java_out=java類輸出目錄 proto的輸入目錄/xxx.proto
生成的java類 類似如下(注:不要手動修改該類):

 

序列化和反序列化:

package com.zhuyun.serialize.java.protobuf;
 
import com.google.protobuf.InvalidProtocolBufferException;
import com.zhuyun.serialize.java.protobuf.StudentOuterClass.Student;
 
public class ProtobufDemo {
    public static void main(String[] args) {
        Student.Builder buidler = Student.newBuilder();
        buidler.setName("Frank");
        buidler.setNumber(123456);
        buidler.setHobby("music");
        Student student = buidler.build();
        System.out.println(student.toString());
        
        //序列化
        byte[] array = student.toByteArray();
        //反序列化
        try {
            Student student1 = Student.parseFrom(array);
            System.out.println(student1.toString());
        } catch (InvalidProtocolBufferException e) {
            e.printStackTrace();
        }   
    }
}
運行結果如下:

 

四、thrift序列化
pom依賴:

<dependency>
      <groupId>org.apache.thrift</groupId>
      <artifactId>libthrift</artifactId>
      <version>0.10.0</version>
</dependency>
 

定義一個Hello schema,文件格式是.thrift,內容如下:

Hello.thrift

namespace java com.zhuyun.serialize.java.thrift
service Hello{
    string helloString(1:string para)
}
 

下載thrift的工具:

http://thrift.apache.org/download

 

使用下載的thrift工具,根據Hello.thrift文件來生成一個java文件:

thrift --gen java Hello.thrift
生成的java類 類似如下(注:不要手動修改該類):

 

接口的實現類如下:

HelloServiceImpl.java

package com.zhuyun.serialize.java.thrift;
 
import org.apache.thrift.TException;
/**
 * @author infi
 * @date 2017/02/21-下午2:13.
 */
public class HelloServiceImpl implements Hello.Iface {
    public String helloString(String para) throws TException {
        return "result:"+para;
    }
}
 

thrift服務端:

HelloServiceServer.java

package com.zhuyun.serialize.java.thrift;
 
import org.apache.thrift.TProcessor;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.server.TServer;
import org.apache.thrift.server.TSimpleServer;
import org.apache.thrift.transport.TServerSocket;
import org.apache.thrift.transport.TTransportException;
 
/**
 * @author infi
 * @date 2017/02/21-下午2:15.
 */
public class HelloServiceServer {
    /**
     * 啓動thrift服務器
     * @param args
     */
    public static void main(String[] args) {
        try {
            System.out.println("服務端開啓....");
            TProcessor tprocessor = new Hello.Processor<Hello.Iface>(new HelloServiceImpl());
            // 簡單的單線程服務模型
            TServerSocket serverTransport = new TServerSocket(9898);
            TServer.Args tArgs = new TServer.Args(serverTransport);
            tArgs.processor(tprocessor);
            tArgs.protocolFactory(new TBinaryProtocol.Factory());
            TServer server = new TSimpleServer(tArgs);
            server.serve();
            }catch (TTransportException e) {
            e.printStackTrace();
        }
    }
}
thrift客戶端:

HelloServiceClient.java

package com.zhuyun.serialize.java.thrift;
 
import org.apache.thrift.TException;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.transport.TSocket;
import org.apache.thrift.transport.TTransport;
import org.apache.thrift.transport.TTransportException;
 
/**
 * @author yogo.wang
 * @date 2017/02/21-下午2:35.
 */
public class HelloServiceClient {
 
    public static void main(String[] args) {
        System.out.println("客戶端啓動....");
        TTransport transport = null;
        try {
            transport = new TSocket("localhost", 9898, 30000);
            // 協議要和服務端一致
            TProtocol protocol = new TBinaryProtocol(transport);
            Hello.Client client = new Hello.Client(protocol);
            transport.open();
            String result = client.helloString("哈哈");
            System.out.println(result);
        } catch (TTransportException e) {
            e.printStackTrace();
        } catch (TException e) {
            e.printStackTrace();
        } finally {
            if (null != transport) {
                transport.close();
            }
        }
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章