海量Web日志分析 用Hadoop提取KPI统计指标
海量Web日志分析 用Hadoop提取KPI統計指標
Hadoop家族系列文章,主要介紹Hadoop家族產品,常用的項目包括Hadoop, Hive, Pig, HBase, Sqoop, Mahout, Zookeeper, Avro, Ambari, Chukwa,新增加的項目包括,YARN, Hcatalog, Oozie, Cassandra, Hama, Whirr, Flume, Bigtop, Crunch, Hue等。
從2011年開始,中國進入大數據風起云涌的時代,以Hadoop為代表的家族軟件,占據了大數據處理的廣闊地盤。開源界及廠商,所有數據軟件,無一不向Hadoop靠攏。Hadoop也從小眾的高富帥領域,變成了大數據開發的標準。在Hadoop原有技術基礎之上,出現了Hadoop家族產品,通過“大數據”概念不斷創新,推出科技進步。
作為IT界的開發人員,我們也要跟上節奏,抓住機遇,跟著Hadoop一起雄起!
關于作者:
- 張丹(Conan), 程序員Java,R,PHP,Javascript
- weibo:@Conan_Z
- blog:?http://blog.fens.me
- email: bsspirit@gmail.com
轉載請注明出處:
http://blog.fens.me/hadoop-mapreduce-log-kpi/
前言
Web日志包含著網站最重要的信息,通過日志分析,我們可以知道網站的訪問量,哪個網頁訪問人數最多,哪個網頁最有價值等。一般中型的網站(10W的PV以上),每天會產生1G以上Web日志文件。大型或超大型的網站,可能每小時就會產生10G的數據量。
對于日志的這種規模的數據,用Hadoop進行日志分析,是最適合不過的了。
目錄
1. Web日志分析概述
Web日志由Web服務器產生,可能是Nginx, Apache, Tomcat等。從Web日志中,我們可以獲取網站每類頁面的PV值(PageView,頁面訪問量)、獨立IP數;稍微復雜一些的,可以計算得出用戶所檢索的關鍵詞排行榜、用戶停留時間最高的頁面等;更復雜的,構建廣告點擊模型、分析用戶行為特征等等。
在Web日志中,每條日志通常代表著用戶的一次訪問行為,例如下面就是一條nginx日志:
222.68.172.190 - - [18/Sep/2013:06:49:57 +0000] "GET /images/my.jpg HTTP/1.1" 200 19939"http://www.angularjs.cn/A00n" "Mozilla/5.0 (Windows NT 6.1)AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.66 Safari/537.36"拆解為以下8個變量
- remote_addr: 記錄客戶端的ip地址, 222.68.172.190
- remote_user: 記錄客戶端用戶名稱, –
- time_local: 記錄訪問時間與時區, [18/Sep/2013:06:49:57 +0000]
- request: 記錄請求的url與http協議, “GET /images/my.jpg HTTP/1.1″
- status: 記錄請求狀態,成功是200, 200
- body_bytes_sent: 記錄發送給客戶端文件主體內容大小, 19939
- http_referer: 用來記錄從那個頁面鏈接訪問過來的, “http://www.angularjs.cn/A00n”
- http_user_agent: 記錄客戶瀏覽器的相關信息, “Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.66 Safari/537.36″
注:要更多的信息,則要用其它手段去獲取,通過js代碼單獨發送請求,使用cookies記錄用戶的訪問信息。
利用這些日志信息,我們可以深入挖掘網站的秘密了。
少量數據的情況
少量數據的情況(10Mb,100Mb,10G),在單機處理尚能忍受的時候,我可以直接利用各種Unix/Linux工具,awk、grep、sort、join等都是日志分析的利器,再配合perl, python,正則表達工,基本就可以解決所有的問題。
例如,我們想從上面提到的nginx日志中得到訪問量最高前10個IP,實現很簡單:
~ cat access.log.10 | awk '{a[$1]++} END {for(b in a) print b"\t"a[b]}' | sort -k2 -r | head -n 10 163.177.71.12 972 101.226.68.137 972 183.195.232.138 971 50.116.27.194 97 14.17.29.86 96 61.135.216.104 94 61.135.216.105 91 61.186.190.41 9 59.39.192.108 9 220.181.51.212 9海量數據的情況
當數據量每天以10G、100G增長的時候,單機處理能力已經不能滿足需求。我們就需要增加系統的復雜性,用計算機集群,存儲陣列來解決。在Hadoop出現之前,海量數據存儲,和海量日志分析都是非常困難的。只有少數一些公司,掌握著高效的并行計算,分步式計算,分步式存儲的核心技術。
Hadoop的出現,大幅度的降低了海量數據處理的門檻,讓小公司甚至是個人都能力,搞定海量數據。并且,Hadoop非常適用于日志分析系統。
2.需求分析:KPI指標設計
下面我們將從一個公司案例出發來全面的解釋,如何用進行海量Web日志分析,提取KPI數據。
案例介紹
某電子商務網站,在線團購業務。每日PV數100w,獨立IP數5w。用戶通常在工作日上午10:00-12:00和下午15:00-18:00訪問量最大。日間主要是通過PC端瀏覽器訪問,休息日及夜間通過移動設備訪問較多。網站搜索瀏量占整個網站的80%,PC用戶不足1%的用戶會消費,移動用戶有5%會消費。
通過簡短的描述,我們可以粗略地看出,這家電商網站的經營狀況,并認識到愿意消費的用戶從哪里來,有哪些潛在的用戶可以挖掘,網站是否存在倒閉風險等。
KPI指標設計
- PV(PageView): 頁面訪問量統計
- IP: 頁面獨立IP的訪問量統計
- Time: 用戶每小時PV的統計
- Source: 用戶來源域名的統計
- Browser: 用戶的訪問設備統計
注:商業保密限制,無法提供電商網站的日志。
下面的內容,將以我的個人網站為例提取數據進行分析。
百度統計,對我個人網站做的統計!http://www.fens.me
基本統計指標:
用戶的訪問設備統計指標:
從商業的角度,個人網站的特征與電商網站不太一樣,沒有轉化率,同時跳出率也比較高。從技術的角度,同樣都關注KPI指標設計。
3.算法模型:Hadoop并行算法
并行算法的設計:
注:找到第一節有定義的8個變量
PV(PageView): 頁面訪問量統計
- Map過程{key:$request,value:1}
- Reduce過程{key:$request,value:求和(sum)}
IP: 頁面獨立IP的訪問量統計
- Map: {key:$request,value:$remote_addr}
- Reduce: {key:$request,value:去重再求和(sum(unique))}
Time: 用戶每小時PV的統計
- Map: {key:$time_local,value:1}
- Reduce: {key:$time_local,value:求和(sum)}
Source: 用戶來源域名的統計
- Map: {key:$http_referer,value:1}
- Reduce: {key:$http_referer,value:求和(sum)}
Browser: 用戶的訪問設備統計
- Map: {key:$http_user_agent,value:1}
- Reduce: {key:$http_user_agent,value:求和(sum)}
4.架構設計:日志KPI系統架構
上圖中,左邊是Application業務系統,右邊是Hadoop的HDFS, MapReduce。
上面這幅圖,我們可以看得更清楚,數據是如何流動的。藍色背景的部分是在Hadoop中的,接下來我們的任務就是完成MapReduce的程序實現。
5.程序開發1:用Maven構建Hadoop項目
請參考文章:用Maven構建Hadoop項目
win7的開發環境 和 Hadoop的運行環境 ,在上面文章中已經介紹過了。
我們需要放日志文件,上傳的HDFS里/user/hdfs/log_kpi/目錄,參考下面的命令操作
~ hadoop fs -mkdir /user/hdfs/log_kpi ~ hadoop fs -copyFromLocal /home/conan/datafiles/access.log.10 /user/hdfs/log_kpi/我已經把整個MapReduce的實現都放到了github上面:
https://github.com/bsspirit/maven_hadoop_template/releases/tag/kpi_v16.程序開發2:MapReduce程序實現
開發流程:
1). 對日志行的解析
新建文件:org.conan.myhadoop.mr.kpi.KPI.java
從日志文件中,取一行通過main函數寫一個簡單的解析測試。
控制臺輸出:
222.68.172.190 - - [18/Sep/2013:06:49:57 +0000] "GET /images/my.jpg HTTP/1.1" 200 19939 "http://www.angularjs.cn/A00n" "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.66 Safari/537.36" valid:true remote_addr:222.68.172.190 remote_user:- time_local:18/Sep/2013:06:49:57 request:/images/my.jpg status:200 body_bytes_sent:19939 http_referer:"http://www.angularjs.cn/A00n" http_user_agent:"Mozilla/5.0 (Windows 2013.09.18:06:49:57 2013091806 www.angularjs.cn我們看到日志行,被正確的解析成了kpi對象的屬性。我們把解析過程,單獨封裝成一個方法。
private static KPI parser(String line) {System.out.println(line);KPI kpi = new KPI();String[] arr = line.split(" ");if (arr.length > 11) {kpi.setRemote_addr(arr[0]);kpi.setRemote_user(arr[1]);kpi.setTime_local(arr[3].substring(1));kpi.setRequest(arr[6]);kpi.setStatus(arr[8]);kpi.setBody_bytes_sent(arr[9]);kpi.setHttp_referer(arr[10]);if (arr.length > 12) {kpi.setHttp_user_agent(arr[11] + " " + arr[12]);} else {kpi.setHttp_user_agent(arr[11]);}if (Integer.parseInt(kpi.getStatus()) >= 400) {// 大于400,HTTP錯誤kpi.setValid(false);}} else {kpi.setValid(false);}return kpi;}對map方法,reduce方法,啟動方法,我們單獨寫一個類來實現
下面將分別介紹MapReduce的實現類:
- PV:org.conan.myhadoop.mr.kpi.KPIPV.java
- IP: org.conan.myhadoop.mr.kpi.KPIIP.java
- Time: org.conan.myhadoop.mr.kpi.KPITime.java
- Browser: org.conan.myhadoop.mr.kpi.KPIBrowser.java
1). PV:org.conan.myhadoop.mr.kpi.KPIPV.java
package org.conan.myhadoop.mr.kpi;import java.io.IOException; import java.util.Iterator;import org.apache.hadoop.fs.Path; import org.apache.hadoop.io.IntWritable; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapred.FileInputFormat; import org.apache.hadoop.mapred.FileOutputFormat; import org.apache.hadoop.mapred.JobClient; import org.apache.hadoop.mapred.JobConf; import org.apache.hadoop.mapred.MapReduceBase; import org.apache.hadoop.mapred.Mapper; import org.apache.hadoop.mapred.OutputCollector; import org.apache.hadoop.mapred.Reducer; import org.apache.hadoop.mapred.Reporter; import org.apache.hadoop.mapred.TextInputFormat; import org.apache.hadoop.mapred.TextOutputFormat;public class KPIPV { public static class KPIPVMapper extends MapReduceBase implements Mapper {private IntWritable one = new IntWritable(1);private Text word = new Text();@Overridepublic void map(Object key, Text value, OutputCollector output, Reporter reporter) throws IOException {KPI kpi = KPI.filterPVs(value.toString());if (kpi.isValid()) {word.set(kpi.getRequest());output.collect(word, one);}}}public static class KPIPVReducer extends MapReduceBase implements Reducer {private IntWritable result = new IntWritable();@Overridepublic void reduce(Text key, Iterator values, OutputCollector output, Reporter reporter) throws IOException {int sum = 0;while (values.hasNext()) {sum += values.next().get();}result.set(sum);output.collect(key, result);}}public static void main(String[] args) throws Exception {String input = "hdfs://192.168.1.210:9000/user/hdfs/log_kpi/";String output = "hdfs://192.168.1.210:9000/user/hdfs/log_kpi/pv";JobConf conf = new JobConf(KPIPV.class);conf.setJobName("KPIPV");conf.addResource("classpath:/hadoop/core-site.xml");conf.addResource("classpath:/hadoop/hdfs-site.xml");conf.addResource("classpath:/hadoop/mapred-site.xml");conf.setMapOutputKeyClass(Text.class);conf.setMapOutputValueClass(IntWritable.class);conf.setOutputKeyClass(Text.class);conf.setOutputValueClass(IntWritable.class);conf.setMapperClass(KPIPVMapper.class);conf.setCombinerClass(KPIPVReducer.class);conf.setReducerClass(KPIPVReducer.class);conf.setInputFormat(TextInputFormat.class);conf.setOutputFormat(TextOutputFormat.class);FileInputFormat.setInputPaths(conf, new Path(input));FileOutputFormat.setOutputPath(conf, new Path(output));JobClient.runJob(conf);System.exit(0);} }在程序中會調用KPI類的方法
KPI kpi = KPI.filterPVs(value.toString());通過filterPVs方法,我們可以實現對PV,更多的控制。
在KPK.java中,增加filterPVs方法
/*** 按page的pv分類*/public static KPI filterPVs(String line) {KPI kpi = parser(line);Set pages = new HashSet();pages.add("/about");pages.add("/black-ip-list/");pages.add("/cassandra-clustor/");pages.add("/finance-rhive-repurchase/");pages.add("/hadoop-family-roadmap/");pages.add("/hadoop-hive-intro/");pages.add("/hadoop-zookeeper-intro/");pages.add("/hadoop-mahout-roadmap/");if (!pages.contains(kpi.getRequest())) {kpi.setValid(false);}return kpi;}在filterPVs方法,我們定義了一個pages的過濾,就是只對這個頁面進行PV統計。
我們運行一下KPIPV.java
2013-10-9 11:53:28 org.apache.hadoop.mapred.MapTask$MapOutputBuffer flush 信息: Starting flush of map output 2013-10-9 11:53:28 org.apache.hadoop.mapred.MapTask$MapOutputBuffer sortAndSpill 信息: Finished spill 0 2013-10-9 11:53:28 org.apache.hadoop.mapred.Task done 信息: Task:attempt_local_0001_m_000000_0 is done. And is in the process of commiting 2013-10-9 11:53:30 org.apache.hadoop.mapred.LocalJobRunner$Job statusUpdate 信息: hdfs://192.168.1.210:9000/user/hdfs/log_kpi/access.log.10:0+3025757 2013-10-9 11:53:30 org.apache.hadoop.mapred.LocalJobRunner$Job statusUpdate 信息: hdfs://192.168.1.210:9000/user/hdfs/log_kpi/access.log.10:0+3025757 2013-10-9 11:53:30 org.apache.hadoop.mapred.Task sendDone 信息: Task 'attempt_local_0001_m_000000_0' done. 2013-10-9 11:53:30 org.apache.hadoop.mapred.Task initialize 信息: Using ResourceCalculatorPlugin : null 2013-10-9 11:53:30 org.apache.hadoop.mapred.LocalJobRunner$Job statusUpdate 信息: 2013-10-9 11:53:30 org.apache.hadoop.mapred.Merger$MergeQueue merge 信息: Merging 1 sorted segments 2013-10-9 11:53:30 org.apache.hadoop.mapred.Merger$MergeQueue merge 信息: Down to the last merge-pass, with 1 segments left of total size: 213 bytes 2013-10-9 11:53:30 org.apache.hadoop.mapred.LocalJobRunner$Job statusUpdate 信息: 2013-10-9 11:53:30 org.apache.hadoop.mapred.Task done 信息: Task:attempt_local_0001_r_000000_0 is done. And is in the process of commiting 2013-10-9 11:53:30 org.apache.hadoop.mapred.LocalJobRunner$Job statusUpdate 信息: 2013-10-9 11:53:30 org.apache.hadoop.mapred.Task commit 信息: Task attempt_local_0001_r_000000_0 is allowed to commit now 2013-10-9 11:53:30 org.apache.hadoop.mapred.FileOutputCommitter commitTask 信息: Saved output of task 'attempt_local_0001_r_000000_0' to hdfs://192.168.1.210:9000/user/hdfs/log_kpi/pv 2013-10-9 11:53:31 org.apache.hadoop.mapred.JobClient monitorAndPrintJob 信息: map 100% reduce 0% 2013-10-9 11:53:33 org.apache.hadoop.mapred.LocalJobRunner$Job statusUpdate 信息: reduce > reduce 2013-10-9 11:53:33 org.apache.hadoop.mapred.Task sendDone 信息: Task 'attempt_local_0001_r_000000_0' done. 2013-10-9 11:53:34 org.apache.hadoop.mapred.JobClient monitorAndPrintJob 信息: map 100% reduce 100% 2013-10-9 11:53:34 org.apache.hadoop.mapred.JobClient monitorAndPrintJob 信息: Job complete: job_local_0001 2013-10-9 11:53:34 org.apache.hadoop.mapred.Counters log 信息: Counters: 20 2013-10-9 11:53:34 org.apache.hadoop.mapred.Counters log 信息: File Input Format Counters 2013-10-9 11:53:34 org.apache.hadoop.mapred.Counters log 信息: Bytes Read=3025757 2013-10-9 11:53:34 org.apache.hadoop.mapred.Counters log 信息: File Output Format Counters 2013-10-9 11:53:34 org.apache.hadoop.mapred.Counters log 信息: Bytes Written=183 2013-10-9 11:53:34 org.apache.hadoop.mapred.Counters log 信息: FileSystemCounters 2013-10-9 11:53:34 org.apache.hadoop.mapred.Counters log 信息: FILE_BYTES_READ=545 2013-10-9 11:53:34 org.apache.hadoop.mapred.Counters log 信息: HDFS_BYTES_READ=6051514 2013-10-9 11:53:34 org.apache.hadoop.mapred.Counters log 信息: FILE_BYTES_WRITTEN=83472 2013-10-9 11:53:34 org.apache.hadoop.mapred.Counters log 信息: HDFS_BYTES_WRITTEN=183 2013-10-9 11:53:34 org.apache.hadoop.mapred.Counters log 信息: Map-Reduce Framework 2013-10-9 11:53:34 org.apache.hadoop.mapred.Counters log 信息: Map output materialized bytes=217 2013-10-9 11:53:34 org.apache.hadoop.mapred.Counters log 信息: Map input records=14619 2013-10-9 11:53:34 org.apache.hadoop.mapred.Counters log 信息: Reduce shuffle bytes=0 2013-10-9 11:53:34 org.apache.hadoop.mapred.Counters log 信息: Spilled Records=16 2013-10-9 11:53:34 org.apache.hadoop.mapred.Counters log 信息: Map output bytes=2004 2013-10-9 11:53:34 org.apache.hadoop.mapred.Counters log 信息: Total committed heap usage (bytes)=376569856 2013-10-9 11:53:34 org.apache.hadoop.mapred.Counters log 信息: Map input bytes=3025757 2013-10-9 11:53:34 org.apache.hadoop.mapred.Counters log 信息: SPLIT_RAW_BYTES=110 2013-10-9 11:53:34 org.apache.hadoop.mapred.Counters log 信息: Combine input records=76 2013-10-9 11:53:34 org.apache.hadoop.mapred.Counters log 信息: Reduce input records=8 2013-10-9 11:53:34 org.apache.hadoop.mapred.Counters log 信息: Reduce input groups=8 2013-10-9 11:53:34 org.apache.hadoop.mapred.Counters log 信息: Combine output records=8 2013-10-9 11:53:34 org.apache.hadoop.mapred.Counters log 信息: Reduce output records=8 2013-10-9 11:53:34 org.apache.hadoop.mapred.Counters log 信息: Map output records=76用hadoop命令查看HDFS文件
~ hadoop fs -cat /user/hdfs/log_kpi/pv/part-00000/about 5 /black-ip-list/ 2 /cassandra-clustor/ 3 /finance-rhive-repurchase/ 13 /hadoop-family-roadmap/ 13 /hadoop-hive-intro/ 14 /hadoop-mahout-roadmap/ 20 /hadoop-zookeeper-intro/ 6這樣我們就得到了,剛剛日志文件中的,指定頁面的PV值。
指定頁面,就像網站的站點地圖一樣,如果沒有指定所有訪問鏈接都會被找出來,通過“站點地圖”的指定,我們可以更容易地找到,我們所需要的信息。
后面,其他的統計指標的提取思路,和PV的實現過程都是類似的,大家可以直接下載源代碼,運行看到結果!!
######################################################
看文字不過癮,作者視頻講解,請訪問網站:http://onbook.me/video
######################################################
轉載請注明出處:
http://blog.fens.me/hadoop-mapreduce-log-kpi/
總結
以上是生活随笔為你收集整理的海量Web日志分析 用Hadoop提取KPI统计指标的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: redis 工具类
- 下一篇: zookeeper 伪分布式安装