本项目分析apache服务器产生的日志,分析pv、独立ip数和跳出率等指标。其实这些指标在第三方系统中都可以检测到,在生产环境中通常用来分析用户交易等核心数据,此处只是用于演示说明日志数据的分析流程。

一、需求分析

  我们可以编写执行的shell脚本,将apache每天产生的日志上传到HDFS中,然后经过数据清洗,hive分析,最后将数据从HDFS导入到mysql中,然后设定计划任务每天定期自动执行分析工作。

1、指标说明

   ▶ PV(Page View):页面浏览量,用户每1次对网站中的每个网页访问均被记录1次。用户对同一页面的多次访问,计算累计访问。通常对网站中资源的访问请求也被计算在PV统计内;

   ▶ 跳出率:只访问了一个页面就离开的浏览量与所产生总浏览量的百分比;

2、实现步骤

   1) 将日志数据上传到HDFS中。如果数据较小,可以通过在shell中编写hdfs命令上传数据到HDFS中,如果数据量较大的话可以通过NFS在另一台机器上上传日志数据。如果服务器比较多,可以使用flume来完成日志收集工作;

   2) 使用MapReduce将HDFS中进行数据清洗。日志原始数据可能在格式上不满足要求,这时候需要通过MapReduce程序来将HDFS中的数据进行清洗过滤,转换成hive统计所需要的格式;

   3) 使用hive对清洗后的数据进行统计分析。利用hive,我们可以很方便地统计分析日志数据。实现建立一个外部分区表,关联到清洗后的HDFS目录中,然后每天数据清洗完后添加当天的分区,最后执行HiveQL完成统计并保存结果;

   4) 利用sqoop将hive统计结果导出到关系数据库如mysql中;

   5) 前端展现统计结果。利用web或其他一些展现手段将mysql中的结果数据直观地展示给用户;

二、详细实现

   本实验分析apache服务器产生的访问日志(样本),其数据格式如“221.194.31.149 - - [27/May/2015:17:52:48 +0800] "GET /static/image/common/star_level1.gif HTTP/1.1" 200 547”,依次表示访问ip、访问时间、访问路径、服务器返回状态。

   本实验实验流程图如下所示:

图1 apache服务器日志分析流程示意图

1、编写MapReduce,将原始数据格式清洗筛选,留下三列:IP、访问时间和访问地址,参考代码:

package com.hicoor.hadoop.logproj;

import java.net.URI;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale; import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.NullWritable;
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.input.TextInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;
import org.apache.hadoop.mapreduce.lib.partition.HashPartitioner;
import org.apache.hadoop.util.GenericOptionsParser; public class DataCleaner { static class LogMapper extends Mapper<LongWritable, Text, Text, LongWritable>{
LogParser logParser = new LogParser();
Text v2 = new Text();
protected void map(LongWritable k1, Text v1, org.apache.hadoop.mapreduce.Mapper<LongWritable,Text,Text,LongWritable>.Context context) throws java.io.IOException ,InterruptedException {
try {
String[] parse = logParser.parse(v1.toString());
//过滤空白行
if(parse != null) {
//过滤结尾的特定格式字符串
if(parse[2].endsWith(" HTTP/1.1")){
parse[2] = parse[2].substring(0, parse[2].length()-" HTTP/1.1".length());
} v2.set(parse[0]+'\t'+parse[1]+'\t'+parse[2]);
}
} catch (Exception e) {
System.out.println("当前行处理出错:"+v1.toString());
}
context.write(v2, new LongWritable(1));
};
} static class LogReduce extends Reducer<Text, LongWritable, Text, NullWritable>{
protected void reduce(Text k2, java.lang.Iterable<LongWritable> v2s, org.apache.hadoop.mapreduce.Reducer<Text,LongWritable,Text,NullWritable>.Context context) throws java.io.IOException ,InterruptedException {
context.write(k2, NullWritable.get());
};
} public static void main(String[] args) throws Exception {
System.setProperty("hadoop.home.dir", "D:/desktop/hadoop-2.6.0");
Configuration conf = new Configuration();
conf.setStrings("dfs.nameservices", "cluster1");
conf.setStrings("dfs.ha.namenodes.cluster1", "hadoop0,hadoop1");
conf.setStrings("dfs.namenode.rpc-address.cluster1.hadoop0", "hadoop0:9000");
conf.setStrings("dfs.namenode.rpc-address.cluster1.hadoop1", "hadoop1:9000");
//必须配置,可以通过该类获取当前处于active状态的namenode
conf.setStrings("dfs.client.failover.proxy.provider.cluster1", "org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider"); Job job = Job.getInstance(conf, "LogDataCleaner"); String[] otherArgs = new GenericOptionsParser(conf, args).getRemainingArgs();
if (otherArgs.length < 2) {
System.err.println("Usage: wordcount <in> [<in>...] <out>");
System.exit(2);
} // 删除已存在的输出目录
String FILE_OUT_PATH = otherArgs[otherArgs.length - 1];
//String FILE_OUT_PATH = "hdfs://cluster1/hmbbs_cleaned/2013_05_30";
FileSystem fileSystem = FileSystem.get(new URI(FILE_OUT_PATH), conf);
if (fileSystem.exists(new Path(FILE_OUT_PATH))) {
fileSystem.delete(new Path(FILE_OUT_PATH), true);
} job.setJarByClass(DataCleaner.class);
//1.1 设置分片函数
job.setInputFormatClass(TextInputFormat.class);
for (int i = 0; i < otherArgs.length - 1; ++i) {
FileInputFormat.addInputPath(job, new Path(otherArgs[i]));
}
//FileInputFormat.addInputPath(job, new Path("hdfs://cluster1/hmbbs_logs/access_2013_05_30.log"));
//1.2 设置map
job.setMapperClass(LogMapper.class);
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(LongWritable.class);
//1.3 设置分区函数
job.setPartitionerClass(HashPartitioner.class);
//job.setNumReduceTasks(3);
//1.4 分组排序
//1.5 规约 job.setOutputFormatClass(TextOutputFormat.class);
FileOutputFormat.setOutputPath(job, new Path(FILE_OUT_PATH)); //2.2 设置Reduce
job.setReducerClass(LogReduce.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(NullWritable.class); job.waitForCompletion(true);
} static class LogParser { public static final SimpleDateFormat FORMAT = new SimpleDateFormat("d/MMM/yyyy:HH:mm:ss", Locale.ENGLISH);
public static final SimpleDateFormat dateformat1=new SimpleDateFormat("yyyyMMddHHmmss");
// public static void main(String[] args) throws ParseException {
// final String S1 = "27.19.74.143 - - [30/May/2013:17:38:20 +0800] \"GET /static/image/common/faq.gif HTTP/1.1\" 200 1127";
// LogParser parser = new LogParser();
// final String[] array = parser.parse(S1);
// System.out.println("样例数据: "+S1);
// System.out.format("解析结果: ip=%s, time=%s, url=%s, status=%s, traffic=%s", array[0], array[1], array[2], array[3], array[4]);
// }
/**
* 解析英文时间字符串
* @param string
* @return
* @throws ParseException
*/
private Date parseDateFormat(String string){
Date parse = null;
try {
parse = FORMAT.parse(string);
} catch (ParseException e) {
e.printStackTrace();
}
return parse;
}
/**
* 解析日志的行记录
* @param line
* @return 数组含有5个元素,分别是ip、时间、url、状态、流量
*/
public String[] parse(String line){
if(line.trim() == "") {
return null;
}
String ip = parseIP(line);
String time = parseTime(line);
String url = parseURL(line);
String status = parseStatus(line);
String traffic = parseTraffic(line); return new String[]{ip, time ,url, status, traffic};
} private String parseTraffic(String line) {
final String trim = line.substring(line.lastIndexOf("\"")+1).trim();
String traffic = trim.split(" ")[1];
return traffic;
}
private String parseStatus(String line) {
final String trim = line.substring(line.lastIndexOf("\"")+1).trim();
String status = trim.split(" ")[0];
return status;
}
private String parseURL(String line) {
final int first = line.indexOf("\"");
final int last = line.lastIndexOf("\"");
String url = line.substring(first+1, last);
return url;
}
private String parseTime(String line) {
final int first = line.indexOf("[");
final int last = line.indexOf("+0800]");
String time = line.substring(first+1,last).trim();
Date date = parseDateFormat(time);
return dateformat1.format(date);
}
private String parseIP(String line) {
String ip = line.split("- -")[0].trim();
return ip;
} }
}

2、mysql中新建表web_logs_stat,包含字段:vtime、pv、ip_n、jumper_n。

3、在shell中创建外部分区表,关联到清洗后的hdfs目录,命令:

   hive> CREATE EXTERNAL TABLE ex_logs(ip string, atime string, url string) PARTITIONED BY (logdate string) ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' LOCATION '/web_cleaned';

4、新建日常自动处理的shell脚本logs_daily_process.sh,内容如下:

#!/bin/sh

#get yesterday format string
yesterday=`date --date='1 days ago' +%Y_%m_%d`
#yesterday=$1 #upload logs to hdfs
hadoop fs -put /apache_logs/access_${yesterday}.log /web_logs #cleaning data
hadoop jar /apache_logs/cleaned.jar /web_logs/access_${yesterday}.log /web_cleaned/${yesterday} 1>/dev/null #alter hive table and then add partition to existed table
hive -e "ALTER TABLE ex_logs ADD PARTITION(logdate='${yesterday}') LOCATION '/web_cleaned/${yesterday}';" #create hive table everyday
hive -e "CREATE TABLE web_pv_${yesterday} AS SELECT COUNT(1) AS PV FROM ex_logs WHERE logdate='${yesterday}';"
hive -e "CREATE TABLE web_ip_${yesterday} AS SELECT COUNT(DISTINCT ip) AS IP FROM ex_logs WHERE logdate='${yesterday}';"
hive -e "CREATE TABLE web_jumper_${yesterday} AS SELECT COUNT(1) AS jumper FROM (SELECT COUNT(ip) AS times FROM ex_logs WHERE logdate='${yesterday}' GROUP BY ip HAVING times=1) e;"
hive -e "CREATE TABLE web_${yesterday} AS SELECT '${yesterday}', a.pv, b.ip, C.jumper FROM web_pv_${yesterday} a JOIN JOIN web_ip_${yesterday} b ON 1=1 JOIN web_jumper_${yesterday} c ON 1=1;" #delete hive tables
hive -e "drop table web_pv_${yesterday};"
hive -e "drop table web_ip_${yesterday};"
hive -e "drop table web_jumper_${yesterday};" #sqoop export to mysql
sqoop export --connect jdbc:mysql://hadoop0:3306/ex_logs --username root --password 123456 --table web_logs_stat --fields-terminated-by '\001' --export-dir '/user/hive/warehouse/web_${yesterday}' #delete hive tables
hive -e "drop table web_${yesterday};"

5、配置计划任务,命令:crontab -e,内容如:* 1 * * * logs_daily_process.sh,表示每天1点周期执行脚本。

hadoop实战 -- 网站日志KPI指标分析的更多相关文章

  1. Hadoop学习笔记—20.网站日志分析项目案例(一)项目介绍

    网站日志分析项目案例(一)项目介绍:当前页面 网站日志分析项目案例(二)数据清洗:http://www.cnblogs.com/edisonchou/p/4458219.html 网站日志分析项目案例 ...

  2. Hadoop学习笔记—20.网站日志分析项目案例(二)数据清洗

    网站日志分析项目案例(一)项目介绍:http://www.cnblogs.com/edisonchou/p/4449082.html 网站日志分析项目案例(二)数据清洗:当前页面 网站日志分析项目案例 ...

  3. Hadoop学习笔记—20.网站日志分析项目案例(三)统计分析

    网站日志分析项目案例(一)项目介绍:http://www.cnblogs.com/edisonchou/p/4449082.html 网站日志分析项目案例(二)数据清洗:http://www.cnbl ...

  4. spark实战之网站日志分析

    前面一篇应该算是比较详细的介绍了spark的基础知识,在了解了一些spark的知识之后相必大家对spark应该不算陌生了吧!如果你之前写过MapReduce,现在对spark也很熟悉的话我想你再也不想 ...

  5. Hadoop网站日志数据清洗——正则表达式实现

    周旭龙前辈的Hadoop学习笔记—网站日志分析项目案例简明.经典,业已成为高校大数据相关专业的实验项目.上周博主也完成了这个实验,不同于周前辈使用特殊符号切割字符串得到数据的做法,博主使用了正则表达式 ...

  6. BI系统与KPI指标的整合分析

    今天我们要说的是信息化时代下关于企业运营的两个热词:BI系统和KPI指标.一直到现在,企业运营的方方面面都在被数据化,成为庞大信息流的一部分,这一庞大的信息流,正以我们自己都尚未完全意识到的速度和规模 ...

  7. python分析apahce网站日志的例子

    有关python实现apahce网站日志分析的方法. 应用到:shell与python数据交互.数据抓取,编码转换 #coding:utf-8 #!/usr/bin/python'''程序说明:apa ...

  8. hive网站日志数据分析

    一.说在前面的话 上一篇,楼主介绍了使用flume集群来模拟网站产生的日志数据收集到hdfs.但我们所采集的日志数据是不规则的,同时也包含了许多无用的日志.当需要分析一些核心指标来满足系统业务决策的时 ...

  9. 升级版:深入浅出Hadoop实战开发(云存储、MapReduce、HBase实战微博、Hive应用、Storm应用)

          Hadoop是一个分布式系统基础架构,由Apache基金会开发.用户可以在不了解分布式底层细节的情况下,开发分布式程序.充分利用集群的威力高速运算和存储.Hadoop实现了一个分布式文件系 ...

随机推荐

  1. 【MongoDB初识】-增删改

    1.切换数据库 admin数据库:use admin test数据库:use test 2.新增: 方法一(首选) c} db.class.save(c) 或者db.class.insert(c) 方 ...

  2. 【转】ArrayList循环遍历并删除元素的常见陷阱

    转自:https://my.oschina.net/u/2249714/blog/612753?p=1 在工作和学习中,经常碰到删除ArrayList里面的某个元素,看似一个很简单的问题,却很容易出b ...

  3. tornado 学习笔记6 Application 源码分析

    Application 是Tornado重要的模块之一,主要是配置访问路由表及其他应用参数的设置. 源代码位于虚拟运行环境文件夹下(我的是env),具体位置为env > lib>sit-p ...

  4. shell在一个大文件找出想要的一段字符串操作技巧

    昨天端午,晚上的时候接了一个电话,我朋友的公司,数据库被两个工作没多久的phper给弄坏了,具体就是把一个字段值,给全表弄成一个了名字了,当然这个是可以配置了禁止全表更新数据库,这下可急坏了,找到我, ...

  5. About_PHP_函数

    关于验证码的完善: //生成干扰线 $posLineX1 = rand(12,50); $posLineX2 = rand(50,110); $posX = rand(10,50); for($i=0 ...

  6. 畅通工程——D

    D. 畅通工程 省政府“畅通工程”的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路相连,只要能间接通过公路可达即可).经过调查评估,得到的统计表中列出了有可能建设公路的若干条道路的 ...

  7. discuz sphinx全文检索搜索引擎方案

    基于discuz的索引配置文件,这个配置文件比较灵活,可以根据不同的需求来配置 # # linuxTone full index search configure file # source lt_p ...

  8. VMware与virtualbox安装centos7连接网络不可达问题解决笔记(连接网络)

    我最初是安装vmware遇到访问不到网络,按网上的配置方法都不能解决.然后我感觉可能跟系统有关,我装的是centos,然后我试着在virtualbox上安装看遇到什么问题. 用virtualbox安装 ...

  9. scrollview 嵌套 折叠效果

    ------------------------------- --@ CreateDate: 2015.08.05 --@ Author:     王成成 --@ FileName:   BaoSh ...

  10. JavaScript之数组方法整理

    Array概述      除了Object类型,最常用的类型:      实质:有序的数据列表,      特性:可以动态的调整数组的大小 创建数组的两种方式 构造函数创建方式  var arr = ...