序列化理解
序列化就是將java對象轉化爲字節文件;反序列化就是將字節文件轉化爲java對象。
比如我們用到Entity都是需要實現序列化接口的。java平臺允許我們在內存中創建一些可服用的對象(例如Entity),也即是在一個JVM進程中允許創建可複用對象。如果在同一個JVM進程中(可以理解爲架構?),如果需要用此對象,直接傳遞該對象的引用就可以了。如果需要將對象保存到磁盤,需要序列化後,從內存保存到磁盤。
但是對於分佈式架構,就相當於多個JVM進程在通信,這時候如果需要對象從一個進程傳到另一個進程,並且可以讀取出來,然後使用,這是不同的內存空間,怎麼辦?這時候就需要用到序列化技術,實現的手段是通過Object流,傳輸的時候,將對象序列化爲二進制流,然後通過網絡傳輸,接收端進程接收到序列化後的字節文件,經過反序列,轉換成對象使用。
序列化技術
現在主流的序列化技術包括:JSON、Hessian、xml、protobuf、kryo、MsgPack、FST、thrift、protostuff、Avro等。
除FST、kryo默認只支持java外,其他序列化技術都是跨語言的。Java序列化機制Serialize接口也是隻支持java語言。
性能對比
分佈式應用系統中,系統之間的通訊質量決定了系統的可用性。在傳輸數據的過程中,數據包越小,時間越少,效率就越高。數據包越小,佔用的寬帶就越少,同等條件下資源利用就會越小。
接下來會對比jackson、FastJson、protobuf、hessian之間的性能。
1、相比json-lib框架,Jackson所依賴的jar包較少,簡單易用並且性能也要相對高些。而且Jackson社區相對比較活躍,更新速度也比較快。
2、Fastjson是一個Java語言編寫的高性能的JSON處理器,由阿里巴巴公司開發。無依賴,不需要例外額外的jar,能夠直接跑在JDK上。
3、protobuf是谷歌出的一款開源項目,序列化文件會經過壓縮,減小存儲空間,從而提高傳輸效率,另外使用了緩衝,可以提高性能
4、hessian序列化的字節文件偏大,會佔用太多存儲空間。
引入四種方式用到的jar包
<!--json實現序列化-谷歌-->
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.13</version>
</dependency>
<!--fastjson實現序列化-阿里-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.51</version>
</dependency>
<!--protobuf實現序列化-谷歌-阿里提供的包jprotobuf-->
<dependency>
<groupId>com.baidu</groupId>
<artifactId>jprotobuf</artifactId>
<version>2.1.2</version>
</dependency>
<!--hessian實現序列化-->
<dependency>
<groupId>com.caucho</groupId>
<artifactId>hessian</artifactId>
<version>4.0.51</version>
</dependency>
創建Person對象
public class Person implements Serializable {
private static final long serialVersionUID = -4015446837465554319L;
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
Jackson實現序列化
public class JsonTest {
//初始化
private static Person init(){
Person person = new Person();
person.setName("Lv");
person.setAge(18);
return person;
}
public static void main(String[] args) throws IOException {
excuteWithJson();
}
/**
* Json實現序列化,驗證效率和總大小
*
* TODO 結果如下:
* Json序列化:67ms:總大小:22
* Json反序列化:Person{name='Lv', age=18, height=2}
* @throws IOException
*/
private static void excuteWithJson() throws IOException {
Person person = init();
//jackson包裏的序列化工具
ObjectMapper mapper = new ObjectMapper();
byte[] bytes = null;
Long start = System.currentTimeMillis();
//循環序列化100次,統計最後的總時間
for (int i= 0; i<100; i++){
bytes = mapper.writeValueAsBytes(person);
}
System.out.println("Json序列化:" + (System.currentTimeMillis() - start) + "ms:" + "總大小:" + bytes.length);
//反序列化
Person person1 = mapper.readValue(bytes, Person.class);
System.out.println("Json反序列化:" +person1);
}
}
結果如下:
FastJson實現序列化
public class JsonTest {
//初始化
private static Person init(){
Person person = new Person();
person.setName("Lv");
person.setAge(18);
return person;
}
public static void main(String[] args) throws IOException {
excuteWithJson();
excuteWithFastJson();
excuteWithHessian();
}
/**
* FastJson實現序列化,驗證效率和總大小
*
* TODO 結果如下:
* FastJson序列化:132ms:總大小:22
* FastJson反序列化:Person{name='Lv', age=18, height=2}
* @throws IOException
*/
private static void excuteWithFastJson() throws IOException {
Person person = init();
String text = null;
Long start = System.currentTimeMillis();
//循環序列化100次,統計最後的總時間
for (int i= 0; i<100; i++){
text = JSON.toJSONString(person);
}
System.out.println("FastJson序列化:" + (System.currentTimeMillis() - start) + "ms:" + "總大小:" + text.getBytes().length);
//反序列化
Person person1 = JSON.parseObject(text,Person.class);
System.out.println("FastJson反序列化:" +person1);
}
結果如下:
Hessian實現序列化
public class JsonTest {
//初始化
private static Person init(){
Person person = new Person();
person.setName("Lv");
person.setAge(18);
return person;
}
public static void main(String[] args) throws IOException {
excuteWithJson();
excuteWithFastJson();
excuteWithHessian();
}
/**
* FastJson實現序列化,驗證效率和總大小
*
* TODO 結果如下:
* Hessian序列化:3ms:總大小:63
* Hessian反序列化:Person{name='Lv', age=18, height=2}
* 效率最高,但是佔用空間最大
* @throws IOException
*/
private static void excuteWithHessian() throws IOException {
Person person = init();
ByteArrayOutputStream os = new ByteArrayOutputStream();
HessianOutput ho = new HessianOutput(os);
Long start = System.currentTimeMillis();
//循環序列化100次,統計最後的總時間,Hessian是累加的,所以需要讀取第一次的大小
for (int i= 0; i<100; i++){
ho.writeObject(person);
if(i==0){
System.out.println(os.toByteArray().length);
}
}
System.out.println("Hessian序列化:" + (System.currentTimeMillis() - start) + "ms:" + "總大小:" + os.toByteArray().length);
//反序列化
HessianInput hi = new HessianInput(new ByteArrayInputStream(os.toByteArray()));
Person person1 = (Person) hi.readObject();
System.out.println("Hessian反序列化:" +person1);
}
}
結果如下
Protobuf實現序列化
Protobuf使用比較麻煩,實體字段需要使用@Protobuf註解
@Protobuf(fieldType = FieldType.STRING)
private String name;
@Protobuf(fieldType = FieldType.INT32)
private int age;
public class ProtobufTest {
//初始化
private static Person init(){
Person person = new Person();
person.setName("Lv");
person.setAge(18);
return person;
}
public static void main(String[] args) throws IOException {
excuteWithProtobuf();
}
/**
* Protobuf實現序列化,驗證效率和總大小
*
* TODO 結果如下:
* Protobuf序列化:10ms:總大小:6
* Protobuf反序列化:Person{name='Lv', age=18}
* 效率高,佔用空間小
* @throws IOException
*/
private static void excuteWithProtobuf() throws IOException {
Person person = init();
Codec<Person> personCodec = ProtobufProxy.create(Person.class,false);
byte[] bytes = null;
Long start = System.currentTimeMillis();
//循環序列化100次,統計最後的總時間
for (int i= 0; i<100; i++){
bytes = personCodec.encode(person);
}
System.out.println("Protobuf序列化:" + (System.currentTimeMillis() - start) + "ms:" + "總大小:" + bytes.length);
//反序列化
Person person1 = personCodec.decode(bytes);
System.out.println("Protobuf反序列化:" +person1);
}
}
結果如下
對比
Json序列化:65ms:總大小:22
Json反序列化:Person{name='Lv', age=18, height=2}
FastJson序列化:143ms:總大小:22
FastJson反序列化:Person{name='Lv', age=18, height=2}
Hessian序列化:2ms:總大小:63
Hessian反序列化:Person{name='Lv', age=18, height=2}
Protobuf序列化:11ms:總大小:6
Protobuf反序列化:Person{name='Lv', age=18}
由以上結果可知:
文件大小:Protobuf最小,傳輸寬帶方法佔有優勢;Hessian最大。
時間:Hessian和Protobuf都小。
總體來講,json是我們常用的,也是最流行的,而Protobuf是後起之秀,性能更好,但是Protobuf的操作比較麻煩。