目錄 java
一、爲何要序列化?apache
二、什麼是序列化?數組
三、爲何不用Java的序列化?服務器
四、爲何序列化對Hadoop很重要?網絡
通常來講,"活的"對象只存在內存裏,關機斷電就沒有了。並且"活的"對象只能由本地的進程使用,不能被髮送到網絡上的另一臺計算機。 然而序列化能夠存儲"活的"對象,能夠將"活的"對象發送到遠程計算機。函數
序列化就是指將對象(實例)轉化爲字節流(字符數組)。反序列化就是將字節流轉化爲對象的逆過程。 因而,若是想把"活的"對象存儲到文件,存儲這串字節便可,若是想把"活的"對象發送到遠程主機,發送這串字節便可,須要對象的時候,作一下反序列化,就能將對象"復活"了。
將對象序列化存儲到文件,術語又叫"持久化"。將對象序列化發送到遠程計算機,術語又叫"數據通訊"。
Java的序列化機制的缺點就是計算量開銷大,且序列化的結果體積大太,有時能達到對象大小的數倍乃至十倍。它的引用機制也會致使大文件不能分割的問題。這些缺點使得Java的序列化機制對Hadoop來講是不合適的。因而Hadoop設計了本身的序列化機制。
由於Hadoop在集羣之間進行通信或者RPC調用的時候,須要序列化,並且要求序列化要快,且體積要小,佔用帶寬要小。因此必須理解Hadoop的序列化機制。
序列化和反序列化在分佈式數據處理領域常常出現:進程通訊和永久存儲。然而Hadoop中各個節點的通訊是經過遠程調用(RPC)實現的,那麼 RPC序列化要求具備如下特色:
緊湊:緊湊的格式能讓咱們能充分利用網絡帶寬,而帶寬是數據中心最稀缺的資源
快速:進程通訊造成了分佈式系統的骨架,因此須要儘可能減小序列化和反序列化的性能開銷,這是基本的
可擴展:協議爲了知足新的需求變化,因此控制客戶端和服務器過程當中,須要直接引進相應的協議,這些是新協議,原序列化方式能支持新的協議報文
互操做:能支持不一樣語言寫的客戶端和服務端進行交互
Hadoop中定義了兩個序列化相關的接口:Writable 接口和 Comparable 接口,這兩個接口能夠合併成一個接口 WritableComparable
下面咱們就瞭解一下這兩個序列化接口:
全部實現了Writable接口的類均可以被序列化和反序列化。 Writable 接口中定義了兩個方法,分別爲write(DataOutput out)和readFields(DataInput in)。write 用於將對象狀態寫入二進制格式的DataOutput流,readFields 用於從二進制格式的 DataInput 流中讀取對象狀態。
1 package org.apache.hadoop.io; 2 3 import java.io.DataOutput; 4 5 import java.io.DataInput; 6 7 import java.io.IOException; 8 9 import org.apache.hadoop.classification.InterfaceAudience; 10 11 import org.apache.hadoop.classification.InterfaceStability; 12 13 public interface Writable { 14 /** 15 16 * 將對象轉換爲字節流並寫入到輸出流out中 17 18 */ 19 20 void write(DataOutput out) throws IOException; 21 22 /** 23 24 * 從輸入流in中讀取字節流反序列化爲對象 25 26 */ 27 28 void readFields(DataInput in) throws IOException; 29 }
對於一個特定的 Writable,咱們能夠對它進行哪些操做呢?
有兩種經常使用操做:賦值和取值,這裏咱們以 IntWritable 爲例來分別說明(IntWritable是對Java的int類型的封裝)
1)經過 set() 函數設置 IntWritable 的值
IntWritable value = new IntWritable();
value.set(588)
相似的,也可使用構造函數來賦值。
IntWritable value = new IntWritable(588);
2)經過get()函數獲取 IntWritable 的值。
int result = value.get();// 這裏獲取的值爲588
全部實現了Comparable的對象均可以和自身相同類型的對象比較大小。該接口定義爲:
1 package java.lang; 2 3 import java.util.*; 4 5 public interface Comparable<T> { 6 /** 7 * 將this對象和對象o進行比較,約定:返回負數爲小於,零爲大於,整數爲大於 8 */ 9 public int compareTo(T o); 10 }
雖然 Hadoop 自帶一系列Writable實現,如IntWritable,LongWritable等,能夠知足一些簡單的數據類型。但有時,複雜的數據類型須要本身自定義實現。經過自定義Writable,可以徹底控制二進制表示和排序順序。
現有的 Hadoop Writable 應用已獲得很好的優化,但爲了對付更復雜的結構,最好建立一個新的 Writable 類型,而不是使用已有的類型。下面咱們來學習一下如何自定義 Writable 類型,以自定義一個Writable 類型TextPair爲例,以下所示
1 import java.io.*; 2 3 import org.apache.hadoop.io.*; 4 5 /** 6 * @ProjectName Serialize 7 * @ClassName TextPair 8 * @Description 自定義Writable類型TextPair 9 * @Author 劉吉超 10 * @Date 2016-04-16 23:59:19 11 */ 12 public class TextPair implements WritableComparable<TextPair> { 13 // Text 類型的實例變量 14 private Text first; 15 // Text 類型的實例變量 16 private Text second; 17 18 public TextPair() { 19 set(new Text(), new Text()); 20 } 21 22 public TextPair(String first, String second) { 23 set(new Text(first), new Text(second)); 24 } 25 26 public TextPair(Text first, Text second) { 27 set(first, second); 28 } 29 30 public void set(Text first, Text second) { 31 this.first = first; 32 this.second = second; 33 } 34 35 public Text getFirst() { 36 return first; 37 } 38 39 public Text getSecond() { 40 return second; 41 } 42 43 @Override 44 // 將對象轉換爲字節流並寫入到輸出流out中 45 public void write(DataOutput out) throws IOException { 46 first.write(out); 47 second.write(out); 48 } 49 50 @Override 51 // 從輸入流in中讀取字節流反序列化爲對象 52 public void readFields(DataInput in) throws IOException { 53 first.readFields(in); 54 second.readFields(in); 55 } 56 57 @Override 58 public int hashCode() { 59 return first.hashCode() * 163 + second.hashCode(); 60 } 61 62 @Override 63 public boolean equals(Object o) { 64 if (o instanceof TextPair) { 65 TextPair tp = (TextPair) o; 66 return first.equals(tp.first) && second.equals(tp.second); 67 } 68 return false; 69 } 70 71 @Override 72 public String toString() { 73 return first + "\t" + second; 74 } 75 76 // 排序 77 @Override 78 public int compareTo(TextPair tp) { 79 int cmp = first.compareTo(tp.first); 80 if (cmp != 0) { 81 return cmp; 82 } 83 return second.compareTo(tp.second); 84 } 85 }
TextPair對象有兩個Text實例變量(first和second)、相關的構造函數、get方法和set方法。 全部的Writable實現都必須有一個默認的構造函數,以便MapReduce框架可以對它們進行實例化,進而調用readFields()方法來填充它們的字段。Writable實例是易變的、常常重用的,因此應該儘可能避免在 write() 或 readFields() 方法中分配對象。
經過委託給每一個 Text 對象自己,TextPair 的 write() 方法依次序列化輸出流中的每個 Text 對象。一樣也經過委託給 Text 對象自己,readFields() 反序列化 輸入流中的字節。DataOutput 和 DataInput 接口有豐富的整套方法用於序列化和反序列化 Java 基本類型,因此在通常狀況下,可以徹底控制 Writable 對象的數據傳輸格式。
正如爲Java寫的任意值對象同樣,會重寫java.lang.Object的hashCode()、equals()和toString()方法。 HashPartitioner使用hashcode()方法來選擇reduce分區,因此應該確保寫一個好的哈希函數來肯定reduce函數的分區在大小上是至關的。
TextPair是WritableComparable的實現,因此它提供了compareTo()方法的實現,加入咱們但願的排序:經過一個一個String逐個排序
若是,您認爲閱讀這篇博客讓您有些收穫,不妨點擊一下右下角的【推薦】。
若是,您但願更容易地發現個人新博客,不妨點擊一下左下角的【關注我】。
若是,您對個人博客所講述的內容有興趣,請繼續關注個人後續博客,我是【劉超★ljc】。
本文版權歸做者和博客園共有,歡迎轉載,但未經做者贊成必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接,不然保留追究法律責任的權利。