一.序列化簡介
什麼是序列化呢?
序列化:對象———》字節序列
反序列化:字節序列——》對象
備註:對象在內存(RAM)當中
字節序列:可以在磁盤(ROM)當中,也可以在網絡當中進行傳輸
序列化的根本緣故:將對象從RAM裏的數據 轉化成ROM裏的數據
二.序列化案例
我們這裏將要編寫的序列化的程序的流程如下圖所示,是一個統計手機耗費總流量的case:
對於這個案例而言。爲什麼需要進行序列化呢?
因爲在第三階段,我們將手機的上行流量和下行流量都分別封裝進了一個對象當中,一個手機號對應兩個流量。因此一個bean對象(就是一個普通的對象,擁有方法,屬性等)當中具有多個數據,因此需要進行序列化。
三.編寫Bean類
現在我們開始封裝這個手機的數據,代碼如下所示。代碼主要是爲了封裝value,也就是手機的上行流量以及下行流量,這裏不處理手機號,不將手機號進行封裝。因爲手機號在我們的mapper階段,我們將其視爲Key同時注意要想把結果顯示在文件中,需要重寫toString(),且用"\t"分開,方便後續用。
import org.apache.hadoop.io.Writable; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; //建立每一個手機號所對應的對象 public class FlowBean implements Writable { private long upFlow;//上行流量 private long downFlow;//下行流量 private long sumFlow;//總流量 //空參構造,爲了後續能夠反射 public FlowBean() { super(); } public FlowBean(long upFlow,long downFlow) { super(); this.upFlow=upFlow; this.downFlow=downFlow; sumFlow=upFlow+upFlow; } //序列化方法,這樣這個對象就可以很方便地進行序列化和反序列化了! //序列化的方法必須和反序列化相同 @Override public void write(DataOutput dataOutput) throws IOException { dataOutput.writeLong(upFlow); dataOutput.writeLong(downFlow); dataOutput.writeLong(sumFlow); } //反序列方法 @Override public void readFields(DataInput dataInput) throws IOException { //必須要求和序列化要求順序一致,順序一致就可以進行接收 upFlow=dataInput.readLong(); downFlow= dataInput.readLong(); sumFlow=dataInput.readLong(); } @Override public String toString() { return upFlow + "\t" + downFlow + "\t" + sumFlow; } public long getUpFlow() { return upFlow; } public void setUpFlow(long upFlow) { this.upFlow = upFlow; } public long getDownFlow() { return downFlow; } public void setDownFlow(long downFlow) { this.downFlow = downFlow; } public long getSumFlow() { return sumFlow; } public void setSumFlow(long sumFlow) { this.sumFlow = sumFlow; } public void set(long upFlow,long downFlow) { upFlow=upFlow; downFlow=downFlow; sumFlow=upFlow+downFlow; } }
在編寫這個bean類當中,我們擁有了一個bean類所有的特徵,比如get/set方法,以及需要擁有的序列化以及反序列化方法,可以在後續調用這個bean對象的時候,更加方便地進行序列化和反序列化。
四.編寫Mapper類
將獲得的手機號碼設定爲key,手機號的上行流量和下行流量分別設定爲value,value使用bean類FlowBean來表示。(因爲有兩個value,而在mapper裏面又只能夠有一個輸出,因此只能使用一個類來代表mapper的輸出(valueout)了)
import org.apache.hadoop.io.LongWritable; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapreduce.Mapper; import java.io.IOException; public class FlowCountMapper extends Mapper<LongWritable, Text,Text,FlowBean> { @Override protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException { Text k=new Text(); FlowBean v=new FlowBean(); //1.獲取一行,tostring方法已經被改寫,因此 String line=value.toString(); //2.切割 String[] fields=line.split("\t"); //3.封裝對象 k.set(fields[1]);//封裝手機號 //封裝 long upFlow= Long.parseLong(fields[fields.length-3]); long downFlow=Long.parseLong(fields[fields.length-2]); v.setUpFlow(upFlow); v.setUpFlow(downFlow); //v.set(); //4.寫出 context.write(k,v); } }
五.編寫Reducer類
import org.apache.hadoop.mapreduce.Reducer; import javax.xml.soap.Text; import java.io.IOException; public class FlowCountReducer extends Reducer<Text,FlowBean,Text,FlowBean> { FlowBean v=new FlowBean(); @Override protected void reduce(Text key, Iterable<FlowBean> values, Context context) throws IOException, InterruptedException { long sum_upFlow=0; long sum_downflow=0; //1.累加求和 for (FlowBean flowBean:values) { sum_upFlow+=flowBean.getUpFlow(); sum_downflow+=flowBean.getDownFlow(); } v.set(sum_upFlow,sum_downflow); //2.寫出 context.write(key,v); } }
六.編寫Driver類
編寫driver類是一個固定的步驟,可以直接根據註釋當中的步驟進行編寫即可,代碼如下所示:
import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.Path; import org.apache.hadoop.mapreduce.Job; import org.apache.hadoop.mapreduce.lib.input.FileInputFormat; import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat; import javax.xml.soap.Text; import java.io.IOException; public class FlowsumDriver { public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException { Configuration configuration=new Configuration(); //1.獲取job對象 Job job=Job.getInstance(configuration); //2.設置jar路徑 job.setJarByClass(FlowsumDriver.class); //3.關聯mapper和reducer job.setMapperClass(FlowCountMapper.class); job.setReducerClass(FlowCountReducer.class); //4.設置mapper輸出的key和value類型 job.setMapOutputKeyClass(Text.class); job.setMapOutputKeyClass(FlowBean.class); //5.設置最終輸出的key和value類型 job.setOutputKeyClass(Text.class); job.setOutputValueClass(FlowBean.class); //6.設置輸出路徑 FileInputFormat.setInputPaths(job, new Path(args[0])); FileOutputFormat.setOutputPath(job,new Path(args[1])); //7.提交job boolean result=job.waitForCompletion(true); System.out.println(result); } }
這樣我們就可以完成這個任務了!