Hadoop-web日誌信息挖掘MapReduce簡單應用以及代碼下載

結合上面兩篇博文的內容,首先提一下hadoop的安裝和配置。
Hadoop-利用java API操作HDFS文件
Hadoop-MapReduce初步應用-統計單詞個數
上面兩篇文章中提到了如何安裝和配置hadoop。以及一些視頻資料的下載。這兩篇文章包括本文的代碼示例均是僞分佈下hadoop的開發完成的。

下面開始本文中題目所說的web日誌信息的挖掘。
首先給出web日誌文件的下載:請猛戳這裏!

有了該文件之後,可以打開看看裏面的內容,內容示例如下:

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\"

這是正常情況下一條web信息日誌所記錄的信息。(關於這裏使用到的日誌信息,請參考原創作者:海量Web日誌分析 用Hadoop提取KPI統計指標 | 粉絲日誌

首先最終原創作者的成果。非常感謝前輩!前輩的項目使用的是eclipse結合Maven開發的工程項目。
本人在原創作者的基礎上,沒有使用Maven,僅僅使用eclipse工具下java工程項目進行的。後期項目完成以後,會附上下載地址供大家下載學習交流。

在開發寫代碼之前,需要把日誌文件上傳到HDFS文件系統中,HDFS上傳命令如下:
這裏寫圖片描述
我這裏吧文件上傳到HDFS根目錄下。

下面開發代碼,有了上面的日誌信息以後,就需要對日誌信息就行解析,比較好的方式就是使用解析類完成,代碼如下;

    private String remote_addr;// 記錄客戶端的ip地址
    private String remote_user;// 記錄客戶端用戶名稱,忽略屬性"-"
    private String time_local;// 記錄訪問時間與時區
    private String request;// 記錄請求的url與http協議
    private String status;// 記錄請求狀態;成功是200
    private String body_bytes_sent;// 記錄發送給客戶端文件主體內容大小
    private String http_referer;// 用來記錄從那個頁面鏈接訪問過來的
    private String http_user_agent;// 記錄客戶瀏覽器的相關信息

    private boolean valid = true;// 判斷數據是否合法

    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;
    }

這裏使用parser方法進行解析。解析得到八個信息值。
有了這些信息值以後,就可以根據這些信息值就行統計。原創作者在代碼中完成了四項統計工作。
分別統計了訪問網站的獨立IP的統計,網頁訪問次數的統計,一天之內每小時的訪問次數統計,和訪問網站的瀏覽器類型的統計

關於統計的信息說明以及如何統計,原創博文中說明的很詳細了,鏈接在此,請點擊:海量Web日誌分析 用Hadoop提取KPI統計指標 作者的博文寫的真是通俗易懂。在此我就不羅嗦了。
下面給出日誌解析工具類的完成代碼如下:

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;

/*
 * KPI Object
 * 日誌文件每一行作爲一條記錄,該類完成每一行日誌的解析
 */
public class KPI {
    private String remote_addr;// 記錄客戶端的ip地址
    private String remote_user;// 記錄客戶端用戶名稱,忽略屬性"-"
    private String time_local;// 記錄訪問時間與時區
    private String request;// 記錄請求的url與http協議
    private String status;// 記錄請求狀態;成功是200
    private String body_bytes_sent;// 記錄發送給客戶端文件主體內容大小
    private String http_referer;// 用來記錄從那個頁面鏈接訪問過來的
    private String http_user_agent;// 記錄客戶瀏覽器的相關信息

    private boolean valid = true;// 判斷數據是否合法

    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;
    }

    /**
     * 按page的pv分類
     */
    public static KPI filterPVs(String line) {
        KPI kpi = parser(line);
        Set<String> pages = new HashSet<String>();
        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;
    }

    /**
     * 按page的獨立ip分類
     */
    public static KPI filterIPs(String line) {
        KPI kpi = parser(line);
        Set<String> pages = new HashSet<String>();
        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;
    }

    /**
     * PV按瀏覽器分類
     */
    public static KPI filterBroswer(String line) {
        return parser(line);
    }

    /**
     * PV按小時分類
     */
    public static KPI filterTime(String line) {
        return parser(line);
    }

    /**
     * PV按訪問域名分類
     */
    public static KPI filterDomain(String line){
        return parser(line);
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("valid:" + this.valid);
        sb.append("\nremote_addr:" + this.remote_addr);
        sb.append("\nremote_user:" + this.remote_user);
        sb.append("\ntime_local:" + this.time_local);
        sb.append("\nrequest:" + this.request);
        sb.append("\nstatus:" + this.status);
        sb.append("\nbody_bytes_sent:" + this.body_bytes_sent);
        sb.append("\nhttp_referer:" + this.http_referer);
        sb.append("\nhttp_user_agent:" + this.http_user_agent);
        return sb.toString();
    }

    public String getRemote_addr() {
        return remote_addr;
    }

    public void setRemote_addr(String remote_addr) {
        this.remote_addr = remote_addr;
    }

    public String getRemote_user() {
        return remote_user;
    }

    public void setRemote_user(String remote_user) {
        this.remote_user = remote_user;
    }

    public String getTime_local() {
        return time_local;
    }

    public Date getTime_local_Date() throws ParseException {
        SimpleDateFormat df = new SimpleDateFormat("dd/MMM/yyyy:HH:mm:ss", Locale.US);
        return df.parse(this.time_local);
    }

    public String getTime_local_Date_hour() throws ParseException{
        SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHH");
        return df.format(this.getTime_local_Date());
    }

    public void setTime_local(String time_local) {
        this.time_local = time_local;
    }

    public String getRequest() {
        return request;
    }

    public void setRequest(String request) {
        this.request = request;
    }

    public String getStatus() {
        return status;
    }

    public void setStatus(String status) {
        this.status = status;
    }

    public String getBody_bytes_sent() {
        return body_bytes_sent;
    }

    public void setBody_bytes_sent(String body_bytes_sent) {
        this.body_bytes_sent = body_bytes_sent;
    }

    public String getHttp_referer() {
        return http_referer;
    }

    public String getHttp_referer_domain(){
        if(http_referer.length()<8){ 
            return http_referer;
        }

        String str=this.http_referer.replace("\"", "").replace("http://", "").replace("https://", "");
        return str.indexOf("/")>0?str.substring(0, str.indexOf("/")):str;
    }

    public void setHttp_referer(String http_referer) {
        this.http_referer = http_referer;
    }

    public String getHttp_user_agent() {
        return http_user_agent;
    }

    public void setHttp_user_agent(String http_user_agent) {
        this.http_user_agent = http_user_agent;
    }

    public boolean isValid() {
        return valid;
    }

    public void setValid(boolean valid) {
        this.valid = valid;
    }

    public static void main(String args[]) {
        String line = "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\"";
        System.out.println(line);
        KPI kpi = new KPI();
        //每一行內容,按空格分割成數組中保存
        String[] arr = line.split(" ");
        //保存訪問的ip地址
        kpi.setRemote_addr(arr[0]);
        //保存訪問的用戶,該值在文件中基本無效,該值爲-
        kpi.setRemote_user(arr[1]);
        //保存訪問時間
        kpi.setTime_local(arr[3].substring(1));
        //保存訪問的請求地址
        kpi.setRequest(arr[6]);
        //保存訪問的請求狀態,正常情況,該值爲200
        kpi.setStatus(arr[8]);
        //保存服務器響應的字節數
        kpi.setBody_bytes_sent(arr[9]);
        //保存從何處訪問來
        kpi.setHttp_referer(arr[10]);
        //保存瀏覽器或者客戶單信息
        kpi.setHttp_user_agent(arr[11] + " " + arr[12]);
        System.out.println(kpi);
        try {
            SimpleDateFormat df = new SimpleDateFormat("yyyy.MM.dd:HH:mm:ss", Locale.US);
            System.out.println(df.format(kpi.getTime_local_Date()));
            System.out.println(kpi.getTime_local_Date_hour());
            System.out.println(kpi.getHttp_referer_domain());
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }

}

代碼中包含了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
 */

有了上面的解析工具類,下面就可以利用MapReduce完成信息的統計。

  • 網站資源的訪問次數的統計

首先看網站每個頁面的訪問次數的統計,在類中,詳細給出了功能性的註釋,具體看代碼:

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;
/**
 * 頁面訪問量統計MapReduce
 * 統計每個頁面的訪問次數
 * 完成的功能類似於單詞統計:
 * 以每個訪問網頁的資源路徑爲鍵,經過mapreduce任務,
 * 最終得到每一個資源路徑的訪問次數
 */
public class KPIPV { 

    public static class KPIPVMapper extends MapReduceBase implements Mapper<Object, Text, Text, IntWritable> {
        private IntWritable one = new IntWritable(1);
        private Text word = new Text();

        @Override
        public void map(Object key, Text value, OutputCollector<Text, IntWritable> output, Reporter reporter) throws IOException {
            KPI kpi = KPI.filterPVs(value.toString());
            if (kpi.isValid()) {
                /**
                 * 以頁面的資源路徑爲鍵,每訪問一次,值爲1
                 * 作爲map任務的輸出
                 */
                word.set(kpi.getRequest());
                output.collect(word, one);
            }
        }
    }

    public static class KPIPVReducer extends MapReduceBase implements Reducer<Text, IntWritable, Text, IntWritable> {
        private IntWritable result = new IntWritable();

        @Override
        public void reduce(Text key, Iterator<IntWritable> values, OutputCollector<Text, IntWritable> 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 {
        /**
         * 對應於HDFS中文件所在的位置路徑
         */
        String input = "hdfs://centos:9000/access.log.10";
        /**
         * 對應HDFS中的輸出文件。文件不能事先存在。否則出錯
         */
        String output = "hdfs://centos:9000/out_kpipv";

        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");
        //設置map輸出的鍵類型,對應於map的輸出以資源路徑字符串作爲鍵
        conf.setMapOutputKeyClass(Text.class);
        //設置map輸出的值類型,對應於map輸出,值爲1
        conf.setMapOutputValueClass(IntWritable.class);
        //設置reduce的輸出鍵類型,和map相同
        conf.setOutputKeyClass(Text.class);
        //設置reduce的輸出值類型,和map相同,不過是累加的值
        conf.setOutputValueClass(IntWritable.class);
        //設置map類
        conf.setMapperClass(KPIPVMapper.class);
        //設置合併函數,該合併函數和reduce完成相同的功能,提升性能,減少map和reduce之間數據傳輸量
        conf.setCombinerClass(KPIPVReducer.class);
        //設置reduce類
        conf.setReducerClass(KPIPVReducer.class);
        //設置輸入文件類型,默認TextInputFormat,該行代碼可省略
        conf.setInputFormat(TextInputFormat.class);
        //設置輸出文件類型,默認TextOutputFormat,該行代碼可省略
        conf.setOutputFormat(TextOutputFormat.class);
        //設置輸入文件路徑
        FileInputFormat.setInputPaths(conf, new Path(input));
        //設置輸出文件路徑。該路徑不能存在,否則出錯!!!
        FileOutputFormat.setOutputPath(conf, new Path(output));
        //運行啓動任務
        JobClient.runJob(conf);
        System.exit(0);
    }

}

代碼中給出了大部分的註釋,網站網頁的訪問次數,通過統計日誌文件的每一行記錄,統計每一行日誌的訪問網站的資源路徑爲鍵,相同的資源路徑累加1,最後即可得到訪問網站網頁的訪問次數。

這裏寫圖片描述
運行代碼,控制檯日誌信息輸出正常,無異常信息。下面看看HDFS文件的輸出情況:
這裏寫圖片描述

out_kpipv輸出文件已經被創建,同時,該文件下還有一個_SUCCESS文件和part-00000文件。分別打開文件查看內容發現,_SUCCESS文件裏面沒有內容,part-00000文件內記錄了結果信息。
前面是資源路徑,後面的數字對應該資源路徑被訪問的次數。

  • 訪問網站的獨立IP統計
    代碼如下:
import java.io.IOException;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import org.apache.hadoop.fs.Path;
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;
/**
 * 統計每個資源路徑的ip訪問MapReduce
 * map以資源路徑爲鍵,以ip爲值輸出
 * reduce完成以資源路徑爲鍵,ip統計值爲值寫入到結果文件中。
 * 
 * 完成的功能類似於單詞統計!!
 */
public class KPIIP {

    public static class KPIIPMapper extends MapReduceBase implements Mapper<Object, Text, Text, Text> {
        private Text word = new Text();
        private Text ips = new Text();

        @Override
        public void map(Object key, Text value, OutputCollector<Text, Text> output, Reporter reporter) throws IOException {
            KPI kpi = KPI.filterIPs(value.toString());
            if (kpi.isValid()) {
                /**
                 * 以請求的資源路徑爲鍵
                 * 以ip爲值
                 */
                word.set(kpi.getRequest());
                ips.set(kpi.getRemote_addr());
                output.collect(word, ips);
            }
        }
    }

    public static class KPIIPReducer extends MapReduceBase implements Reducer<Text, Text, Text, Text> {
        private Text result = new Text();
        private Set<String> count = new HashSet<String>();

        @Override
        public void reduce(Text key, Iterator<Text> values, OutputCollector<Text, Text> output, Reporter reporter) throws IOException {
            /**
             * 以資源路徑爲鍵
             * 以相同資源路徑的ip值的個數,添加入set集合中作爲值
             * 寫入到結果文件中
             */
            while (values.hasNext()) {
                count.add(values.next().toString());
            }
            //結果集中存放的是獨立ip的個數
            result.set(String.valueOf(count.size()));
            output.collect(key, result);
        }
    }

    public static void main(String[] args) throws Exception {
        String input = "hdfs://centos:9000/access.log.10";
        String output = "hdfs://centos:9000/out_kpiip";

        JobConf conf = new JobConf(KPIIP.class);
        conf.setJobName("KPIIP");
//        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(Text.class);

        conf.setOutputKeyClass(Text.class);
        conf.setOutputValueClass(Text.class);

        conf.setMapperClass(KPIIPMapper.class);
        conf.setCombinerClass(KPIIPReducer.class);
        conf.setReducerClass(KPIIPReducer.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);
    }

}

有了第一個mapreduce的完成,下面的三個類你會發現大同小異,所以代碼中沒有給出註釋,註釋部分請參考第一個網站資源訪問次數的統計部分。網站的獨立ip的統計,是根據日誌文件中每一條日誌信息中訪問資源的ip的個數進行統計的。以資源路徑爲鍵,不同的ip值進行累加即可得出結果。
運行代碼結果如下:
這裏寫圖片描述
out_kpiip文件夾被創建,下面看該文件夾下的內容:
這裏寫圖片描述
輸出結果文件中,前面對應資源路徑,後面數字對應獨立ip的個數。

  • 每小時訪問網站的次數統計
    代碼如下:
import java.io.IOException;
import java.text.ParseException;
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 KPITime {
    public static class KPITimeMapper extends MapReduceBase implements Mapper<Object, Text, Text, IntWritable> {
        private IntWritable one = new IntWritable(1);
        private Text word = new Text();

        @Override
        public void map(Object key, Text value, OutputCollector<Text, IntWritable> output, Reporter reporter) throws IOException {
            KPI kpi = KPI.filterBroswer(value.toString());
            if (kpi.isValid()) {
                try {
                    word.set(kpi.getTime_local_Date_hour());
                    output.collect(word, one);
                } catch (ParseException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static class KPITimeReducer extends MapReduceBase implements Reducer<Text, IntWritable, Text, IntWritable> {
        private IntWritable result = new IntWritable();
        @Override
        public void reduce(Text key, Iterator<IntWritable> values, OutputCollector<Text, IntWritable> 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://centos:9000/access.log.10";
        String output = "hdfs://centos:9000/out_kpitime";

        JobConf conf = new JobConf(KPITime.class);
        conf.setJobName("KPITime");
//        conf.addResource("classpath:/hadoop/core-site.xml");
//        conf.addResource("classpath:/hadoop/hdfs-site.xml");
//        conf.addResource("classpath:/hadoop/mapred-site.xml");

        conf.setOutputKeyClass(Text.class);
        conf.setOutputValueClass(IntWritable.class);

        conf.setMapperClass(KPITimeMapper.class);
        conf.setCombinerClass(KPITimeReducer.class);
        conf.setReducerClass(KPITimeReducer.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);
    }
}

每小時訪問網站的次數統計是以每小時時間爲鍵,每小時時間段內的訪問次數進行累加即可得到結果。
運行代碼如下:
這裏寫圖片描述
out_kpitime文件夾創建。下面查看文件夾內容:
這裏寫圖片描述
可以看到日誌文件前面一行對應2013年9月18日這一天每一小時的時間信息,後面的數字對應該一小時內網站的訪問次數。

  • 訪問網站的瀏覽器類型統計
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;
/**
 * 瀏覽器統計MapReduce
 * 
 */
public class KPIBrowser {

    public static class KPIBrowserMapper extends MapReduceBase implements Mapper<Object, Text, Text, IntWritable> {
        private IntWritable one = new IntWritable(1);
        private Text word = new Text();

        @Override
        public void map(Object key, Text value, OutputCollector<Text, IntWritable> output, Reporter reporter) throws IOException {
            KPI kpi = KPI.filterBroswer(value.toString());
            if (kpi.isValid()) {

                word.set(kpi.getHttp_user_agent());
                output.collect(word, one);
            }
        }
    }

    public static class KPIBrowserReducer extends MapReduceBase implements Reducer<Text, IntWritable, Text, IntWritable> {
        private IntWritable result = new IntWritable();

        @Override
        public void reduce(Text key, Iterator<IntWritable> values, OutputCollector<Text, IntWritable> 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://centos:9000/access.log.10";
        String output = "hdfs://centos:9000/out_kpibrowser";

        JobConf conf = new JobConf(KPIBrowser.class);
        conf.setJobName("KPIBrowser");
//        conf.addResource("classpath:/hadoop/core-site.xml");
//        conf.addResource("classpath:/hadoop/hdfs-site.xml");
//        conf.addResource("classpath:/hadoop/mapred-site.xml");

        conf.setOutputKeyClass(Text.class);
        conf.setOutputValueClass(IntWritable.class);

        conf.setMapperClass(KPIBrowserMapper.class);
        conf.setCombinerClass(KPIBrowserReducer.class);
        conf.setReducerClass(KPIBrowserReducer.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);
    }
}

直接看結果:
這裏寫圖片描述
結果中輸出了各種客戶端對網站的訪問情況。

結束!
原作者的文章地址:http://blog.fens.me/hadoop-mapreduce-log-kpi/
原作者的代碼使用eclipse和maven結合使用的。我在此基礎上,沒有使用maven,而是使用eclipse創建java工程完成。

本項目的代碼地址:請猛戳這裏(歡迎關注我的GITHUB)
項目使用eclipse構建。方便易用,代碼註釋詳細。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章