標籤:
本文是結合hadoop中的mapreduce來對使用者資料進行分析,統計使用者的手機號碼、上行流量、下行流量、總流量的資訊,同時可以按照總流量大小對使用者進行分組排序等。是一個非常簡潔易用的hadoop項目,主要使用者進一步加強對MapReduce的理解及實際應用。文末提供來源資料採集檔案和系統源碼。
本案例非常適合hadoop初級人員學習以及想入門大資料、雲端運算、資料分析等領域的朋友進行學習。
一、待分析的資料來源
以下是一個待分析的文字檔,裡面有非常多的使用者瀏覽資訊,保擴使用者手機號碼,上網時間,機器序號,訪問的IP,訪問的網站,上行流量,下行流量,總流量等資訊。這裡只截取一小段,具體檔案在文末提供下載連結。
二、準系統實現想要統計出使用者的上行流量、下行流量、總流量資訊,我們需要建立一個bean類來對資料進行封裝。於是建立應該Java工程,導包,或者直接建立一個MapReduce工程。在這裡面建立一個FlowBean.java檔案。
private long upFlow;private long dFlow;private long sumFlow;
然後就是各種右鍵產生get,set方法,還要toString(),以及產生建構函式,(千萬記得要產生一個空的建構函式,不然後面進行分析的時候會報錯)。完整代碼如下:
package cn.tf.flow;import java.io.DataInput;import java.io.DataOutput;import java.io.IOException;import org.apache.hadoop.io.Writable;import org.apache.hadoop.io.WritableComparable;public class FlowBean implements WritableComparable<FlowBean>{private long upFlow;private long dFlow;private long sumFlow;public long getUpFlow() {return upFlow;}public void setUpFlow(long upFlow) {this.upFlow = upFlow;}public long getdFlow() {return dFlow;}public void setdFlow(long dFlow) {this.dFlow = dFlow;}public long getSumFlow() {return sumFlow;}public void setSumFlow(long sumFlow) {this.sumFlow = sumFlow;}public FlowBean(long upFlow, long dFlow) {super();this.upFlow = upFlow;this.dFlow = dFlow;this.sumFlow = upFlow+dFlow;}@Overridepublic void readFields(DataInput in) throws IOException {upFlow=in.readLong();dFlow=in.readLong();sumFlow=in.readLong();}@Overridepublic void write(DataOutput out) throws IOException {out.writeLong(upFlow);out.writeLong(dFlow);out.writeLong(sumFlow);}public FlowBean() {super();}@Overridepublic String toString() { return upFlow + "\t" + dFlow + "\t" + sumFlow;}@Overridepublic int compareTo(FlowBean o) {return this.sumFlow>o.getSumFlow() ? -1:1;}}
然後就是這個統計的代碼了,建立一個FlowCount.java.在這個類裡面,我直接把Mapper和Reduce寫在同一個類裡面了,如果按規範的要求應該是要分開寫的。在mapper中,擷取後面三段資料的值,所以我的這裡length-2,length-3.
public static class FlowCountMapper extends Mapper<LongWritable, Text, Text, FlowBean> {@Overrideprotected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {// 拿到這行的內容轉成stringString line = value.toString();String[] fields = StringUtils.split(line, "\t");try {if (fields.length > 3) {// 獲得手機號及上下行流量欄位值String phone = fields[1];long upFlow = Long.parseLong(fields[fields.length - 3]);long dFlow = Long.parseLong(fields[fields.length - 2]);// 輸出這一行的處理結果,key為手機號,value為流量資訊beancontext.write(new Text(phone), new FlowBean(upFlow, dFlow));} else {return;}} catch (Exception e) {}}}
在reduce中隊資料進行整理,統計
public static class FlowCountReducer extends Reducer<Text, FlowBean, Text, FlowBean> {@Overrideprotected void reduce(Text key, Iterable<FlowBean> values, Context context) throws IOException, InterruptedException {long upSum = 0;long dSum = 0;for (FlowBean bean : values) {upSum += bean.getUpFlow();dSum += bean.getdFlow();}FlowBean resultBean = new FlowBean(upSum, dSum);context.write(key, resultBean);}}
最後在main方法中調用執行。
public static void main(String[] args) throws Exception {Configuration conf = new Configuration();Job job = Job.getInstance(conf);job.setJarByClass(FlowCount.class);job.setMapperClass(FlowCountMapper.class);job.setReducerClass(FlowCountReducer.class);job.setMapOutputKeyClass(Text.class);job.setMapOutputValueClass(FlowBean.class);job.setOutputKeyClass(Text.class);job.setOutputValueClass(FlowBean.class);FileInputFormat.setInputPaths(job, new Path(args[0]));FileOutputFormat.setOutputPath(job, new Path(args[1]));boolean res = job.waitForCompletion(true);System.exit(res ? 0 : 1);}
當然啦,還需要先在你的hdfs根目錄中建立/flow/data資料,然後我那個使用者的資料來源上傳上去。
bin/hadoop fs -mkdir -p /flow/data bin/hadoop fs -put HTTP_20130313143750.dat /flow/data bin/hadoop jar ../lx/flow.jar
把上面這個MapReduce工程打包成一個jar檔案,然後用hadoop來執行這個jar檔案。例如我放在~/hadoop/lx/flow.jar,然後再hadoop安裝目錄中執行
bin/hadoop jar ../lx/flowsort.jar cn/tf/flow/FlowCount /flow/data /flow/output
最後執行結果如下:
在這整過過程中,我們是有yarnchild的進程在執行的,如所示:當整個過程執行完畢之後yarnchild也會自動結束。
三、按總流量從大到小排序
如果你上面這個基本操作以及完成了的話,按總流量排序就非常簡單了。我們建立一個FlowCountSort.java.
全部代碼如下:
package cn.tf.flow;import java.io.IOException;import org.apache.commons.lang.StringUtils;import org.apache.hadoop.conf.Configuration;import org.apache.hadoop.fs.Path;import org.apache.hadoop.io.LongWritable;import org.apache.hadoop.io.Text;import org.apache.hadoop.mapreduce.Job;import org.apache.hadoop.mapreduce.Mapper;import org.apache.hadoop.mapreduce.Reducer;import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;public class FlowCountSort {public static class FlowCountSortMapper extends Mapper<LongWritable, Text, FlowBean, Text>{@Overrideprotected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {String line=value.toString();String[] fields=StringUtils.split(line,"\t");String phone=fields[0];long upSum=Long.parseLong(fields[1]);long dSum=Long.parseLong(fields[2]);FlowBean sumBean=new FlowBean(upSum,dSum);context.write(sumBean, new Text(phone));}}public static class FlowCountSortReducer extends Reducer<FlowBean, Text, Text, FlowBean>{//進來的“一組”資料就是一個手機的流量bean和手機號@Overrideprotected void reduce(FlowBean key, Iterable<Text> values, Context context) throws IOException, InterruptedException {context.write(values.iterator().next(), key);}}public static void main(String[] args) throws Exception {Configuration conf = new Configuration();Job job = Job.getInstance(conf);job.setJarByClass(FlowCountSort.class);job.setMapperClass(FlowCountSortMapper.class);job.setReducerClass(FlowCountSortReducer.class);job.setMapOutputKeyClass(FlowBean.class);job.setMapOutputValueClass(Text.class);job.setOutputKeyClass(Text.class);job.setOutputValueClass(FlowBean.class);FileInputFormat.setInputPaths(job, new Path(args[0]));FileOutputFormat.setOutputPath(job, new Path(args[1]));boolean res = job.waitForCompletion(true);System.exit(res ? 0 : 1);}}
這個主要就是使用了FlowBean.java中的代碼來實現的,主要是繼承了WritableComparable<FlowBean>介面來實現,然後重寫了compareTo()方法。
@Overridepublic int compareTo(FlowBean o) {return this.sumFlow>o.getSumFlow() ? -1:1;}
按照同樣的方法對這個檔案打成jar包,然後使用hadoop的相關語句進行執行就可以了。
bin/hadoop jar ../lx/flowsort.jar cn/tf/flow/FlowCountSort /flow/output /flow/sortoutput
結果圖:
四、按使用者號碼地區進行分類
流量匯總之後的結果需要按照省份輸出到不同的結果檔案中,需要解決兩個問題:
1、如何讓mr的最終結果產生多個檔案: 原理:MR中的結果檔案數量由reduce
task的數量絕對,是一一對應的 做法:在代碼中指定reduce task的數量
2、如何讓手機號進入正確的檔案 原理:讓不同手機號資料發給正確的reduce task,就進入了正確的結果檔案
要自訂MR中的分區partition的機制(預設的機制是按照kv中k的hashcode%reducetask數)
做法:自訂一個類來幹預MR的分區策略——Partitioner的自訂實作類別
主要代碼與前面的排序是非常類似的,只要在main方法中添加如下兩行代碼就可以了。
//指定自訂的partitionerjob.setPartitionerClass(ProvincePartioner.class);job.setNumReduceTasks(5);
這裡我們需要建立一個ProvincePartioner.java來處理號碼分類的邏輯。
public class ProvincePartioner extends Partitioner<Text, FlowBean>{private static HashMap<String, Integer> provinceMap = new HashMap<String, Integer>();static {provinceMap.put("135", 0);provinceMap.put("136", 1);provinceMap.put("137", 2);provinceMap.put("138", 3);}@Overridepublic int getPartition(Text key, FlowBean value, int numPartitions) {String prefix = key.toString().substring(0, 3);Integer partNum = provinceMap.get(prefix);if(partNum == null) partNum=4;return partNum;}}
執行方法和前面也是一樣的。從執行的流程中我們可以看到這裡啟動了5個reduce task,因為我這裡資料量比較小,所以只啟動了一個map task。
到這裡,整個使用者流量分析系統就全部結束了。關於大資料的更多內容,歡迎關注。點擊左上方頭像下方“點擊關注".感謝您的支援!
資料來源:http://download.csdn.net/detail/sdksdk0/9545935
源碼項目地址:https://github.com/sdksdk0/HDFS_MapReduce
大資料實戰:使用者流量分析系統