寫在開頭:今天主要寫一下MapReduce在日誌流量統計方面的實踐,數據結構比較簡單,主要是一個使用思路。
學習內容安排
今天我們將對用戶使用流量數據進行分析,同樣按照上一節的MapReduce計算流程來編寫代碼。在進行代碼編寫前我們首先來看一下數據的樣子,以下數據均爲虛構的,
在上面的數據中可以看到第一列不太清楚是啥,第二列是手機號,第三四五列是訪問的信息方面,後面的倒數第三與導數第二的數據就是本文今天最關注的上行流量與下行流量。今天的任務就是分別展示上下行流量並計算總流量。
MapReduce之流量統計
這個問題與昨天的問題有一個不同之處在於,詞頻統計在Map輸出是以鍵值對的形式輸出,<text,次數>,而在此處需要的不是次數,而是對應值相加,而且一個Key對應的上下行流量兩個值,也就是在鍵值對中需要儲存電話號碼、上行流量、下行流量三個值,這個就是今天的重點。爲了解決這個問題需要重新構造一個對象來儲存上下行流量,代碼如下,
定義流量類FlowBean
package FlowSum;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import org.apache.hadoop.io.Writable;
public class FlowBean implements Writable{
String mobile;
long up;
long down;
long sum;
public FlowBean(){}
public FlowBean(String mobile, long up, long down) {
this.mobile = mobile;
this.up = up;
this.down = down;
this.sum = up + down;
}
public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
public long getUp() {
return up;
}
public void setUp(long up) {
this.up = up;
}
public long getDown() {
return down;
}
public void setDown(long down) {
this.down = down;
}
public long getSum() {
return sum;
}
public void setSum(long sum) {
this.sum = sum;
}
public String toString(){
return " " + up + ":" +down + ":" +sum;
}
@Override
public void write(DataOutput out) throws IOException {
//hadoop字符可以傳遞,對象需要實現序列化和反序列化這樣才能讀和寫
out.writeUTF(mobile);
out.writeLong(up);
out.writeLong(down);
out.writeLong(sum);
}
@Override
public void readFields(DataInput in) throws IOException {
//序列化
mobile = in.readUTF();
up = in.readLong();
down = in.readLong();
sum = in.readLong();
}
}
在代碼最下端實現的兩個類表示的是將數據進行序列化與反序列化,這樣才能進行讀寫操作,至於爲什麼要序列化與反序列化,可以點擊查看這篇文章的解釋,序列化與反序列化。FlowBean類用於定義鍵值對中值的類型。
Map函數編寫
那在本文中我們不直接從本地上傳到HDFS文件,而是先用SecureFX將本地文件上傳到Linux裏,然後通過copyFromLocal類似語句將Linux文件上傳到HDFS裏,對於編寫Map仍然是按行讀取數據,輸出的時候就是電話號碼,加上剛剛定義的FlowBean函數,
package FlowSum;
import java.io.IOException;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
public class FlowMapper extends Mapper<LongWritable, Text, Text, FlowBean>{
//序列號,1行,輸出電話號碼,輸出攜帶很多值的對象
@Override
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
String line = value.toString();
String[] fields = line.split(" ");
String mobile = fields[1];
long up = Long.parseLong(fields[6]);
long down = Long.parseLong(fields[7]);
context.write(new Text(mobile), new FlowBean(mobile, up, down));
}
}
Reduce函數編寫
在Reduce函數裏就需要將傳回來的FlowBean類中的上下行流量進行加和計算,
package FlowSum;
import java.io.IOException;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
public class FlowReducer extends Reducer<Text, FlowBean, Text, FlowBean>{
//傳過來的是什麼,再要傳出去的兩個鍵值對是什麼
@Override
//框架傳遞過來的是一組數據<13808124758, {FlowBean,FlowBean....}>;就調用一次reduce(),根據需求遍歷Bean,進行累加求和。
protected void reduce(Text key, Iterable<FlowBean> values, Context context) throws IOException, InterruptedException {
long up_sum = 0;
long down_sum = 0;
for (FlowBean value : values) {
up_sum = up_sum + value.getUp();
down_sum = down_sum + value.getDown();
}
context.write(key, new FlowBean(key.toString(), up_sum, down_sum));
}
}
作業函數編輯
最後通過作業的調度,完成計算並進行輸出,與上一節相似,這裏的flowoutput一定要先確認是否存在,如果存在就要把這個文件刪除,因爲這個文件是軟件運行自動生成的,要把之前的刪掉。
package FlowSum;
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.log4j.BasicConfigurator;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
public class FlowRunner {
public static void main(String[] args) throws IOException, InterruptedException, ClassNotFoundException {
BasicConfigurator.configure();
Configuration con = new Configuration();
Job job = Job.getInstance(con);
job.setJarByClass(FlowRunner.class);
job.setMapperClass(FlowMapper.class);
job.setReducerClass(FlowReducer.class);
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(FlowBean.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(FlowBean.class);
FileInputFormat.setInputPaths(job, new Path("hdfs://192.168.0.23:9000/user/root/flowinput"));
FileOutputFormat.setOutputPath(job, new Path("hdfs://192.168.0.23:9000/user/root/flowoutput"));
job.waitForCompletion(true);
}
}
結果展示
可以看到各個用戶在瀏覽上的上下行和總流量
*****
結語
這兩天都主要展示的是一些簡單的MapReduce的應用,但可能考慮到後面使用的並不是很多,具體的更新結合情況再說吧,後面可能還是主要在數據科學實戰和機器學習上。
謝謝閱讀
參考
1.序列化與反序列化