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构建。方便易用,代码注释详细。

  • 3
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值