MapReduce编程案例之电商网站日志的行为分析

简介: 笔记

一、需求分析


1.网站数据分析的四个指标:


PV:PageView ,浏览量


用户每打开一个网页就会被记录1次浏览量,多次打开同一个页面浏览量累计加一


UV:Unique Visitor 独立访客数


同一用户多次访问,独立访客数只算一次


VV:visitor view,访客的访问次数


同一用户完成浏览并关闭该网站时,访客的访问次数算一次


IP:独立IP数


同一IP不管访问了几个页面,独立IP数均为1


2.各个省份PV的统计:

我们的需求分析是统计网站日志文件中的各省份ID出现的次数,比如客户A访问了该网站,省份ID是5,客户B的省份ID是6,客户C的省份ID是8,客户D的省份ID是10,客户E的省份ID是5,客户F的省份ID是6…这样分析类比过来其实就是词频统计,得出的结果也是ID为5的有2人,ID为6的为2人,ID为8的为1人…


根据需求分析,下面为mapreduce各阶段的数据类型

  map -> input  <LongWritable, Text>
  map -> output  <IntWritable, IntWritable>
  reduce -> input  <IntWritable, IntWritable>
  reduce -> output  <IntWritable, IntWritable>


二、程序编写


在编写程序之前,我们需要分析一下每行数据各字段的含义,在编写程序的过程中对数据进行预处理

20.png

示例代码:

package com.kfk.hadoop.mr;
import org.apache.commons.lang.StringUtils;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
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.output.FileOutputFormat;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;
import java.io.IOException;
/**
 * @author : 蔡政洁
 * @email :caizhengjie888@icloud.com
 * @date : 2020/10/9
 * @time : 7:07 下午
 */
public class WebPV extends Configured implements Tool {
    /**
     * map
     * TODO
     */
    public static class WebPvMapper extends Mapper<LongWritable, Text,IntWritable, IntWritable>{
        // 创建map输出的对象
        private static final IntWritable mapOutValue = new IntWritable(1);
        private static final IntWritable mapOutKey = new IntWritable();
        @Override
        public void setup(Context context) throws IOException, InterruptedException {
            // TODO
        }
        @Override
        public void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
            // 每一行数据按"\t"分割
            String[] values = value.toString().split("\t");
            // 数据预处理:每一行数据少于30个字段就过滤掉
            if (values.length<30){
                // 统计每一行数据少于30个字段的个数
                context.getCounter("WEBPV_COUNTERS","LENGTH_LT30_COUNTER").increment(1);
                return;
            }
            String provinceIdValue = values[23];
            String url = values[1];
            // provinceIdValue为空的话将数据过滤掉
            if (StringUtils.isBlank(provinceIdValue)){
                // 统计provinceIdValue为空的个数
                context.getCounter("WEBPV_COUNTERS","PROVINCEID_ISBLACK_COUNTER").increment(1);
                return;
            }
            // url为空的话。将数据过滤掉
            if (StringUtils.isBlank(url)){
                // 统计url为空的个数
                context.getCounter("WEBPV_COUNTERS","URL_ISBLACK_COUNTER").increment(1);
                return;
            }
            // 如果provinceIdValue不能转换成Integer型,则将数据过滤
            int provinceId = 0;
            try{
                provinceId = Integer.valueOf(provinceIdValue);
            }catch (Exception e){
                // 统计provinceIdValue不能转换成Integer型的个数
                context.getCounter("WEBPV_COUNTERS","PROVINCEID_VALIDATE_COUNTER").increment(1);
                return;
            }
            // 将provinceId设置为map输出的key
            mapOutKey.set(provinceId);
            // map输出端的key和value
            context.write(mapOutKey,mapOutValue);
        }
        @Override
        public void cleanup(Context context) throws IOException, InterruptedException {
            // TODO
        }
    }
    /**
     * reduce
     * TODO
     */
    public static class WebPvReducer extends Reducer<IntWritable,IntWritable,IntWritable,IntWritable>{
        // 创建reduce输出的对象
        private static final IntWritable reduceOutValue = new IntWritable();
        @Override
        public void setup(Context context) throws IOException, InterruptedException {
            // TODO
        }
        @Override
        public void reduce(IntWritable key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
            // 对reduce输入的value求和
            int sum = 0;
            for (IntWritable value:values){
                sum += value.get();
            }
            // 将sum设置为reduce输出的key
            reduceOutValue.set(sum);
            // reduce输出端的key和value
            context.write(key,reduceOutValue);
            // 打印出reduce输出端的key和value的值
            System.out.println("Reduce out == KeyOut: "+key+"   ValueOut: "+reduceOutValue);
        }
        @Override
        public void cleanup(Context context) throws IOException, InterruptedException {
            // TODO
        }
    }
    /**
     * run
     * @param args
     * @return
     * @throws IOException
     * @throws ClassNotFoundException
     * @throws InterruptedException
     */
    public int run(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
        // 1) get conf
        Configuration configuration = this.getConf();
        // 2) create job
        Job job = Job.getInstance(configuration,this.getClass().getSimpleName());
        job.setJarByClass(this.getClass());
        // 3.1) input,指定job的输入
        Path path = new Path(args[0]);
        FileInputFormat.addInputPath(job,path);
        // 3.2) map,指定job的mapper和输出的类型
        job.setMapperClass(WebPvMapper.class);
        job.setMapOutputKeyClass(IntWritable.class);
        job.setMapOutputValueClass(IntWritable.class);
        // 1.分区
        // job.setPartitionerClass();
        // 2.排序
        // job.setSortComparatorClass();
        // 3.combiner -可选项
        job.setCombinerClass(WebPvReducer.class);
        // 4.compress -可配置
        // configuration.set("mapreduce.map.output.compress","true");
        // 使用的SnappyCodec压缩算法
        // configuration.set("mapreduce.map.output.compress.codec","org.apache.hadoop.io.compress.SnappyCodec");
        // 5.分组
        // job.setGroupingComparatorClass();
        // 6.设置reduce的数量
        // job.setNumReduceTasks(2);
        // 3.3) reduce,指定job的reducer和输出类型
        job.setReducerClass(WebPvReducer.class);
        job.setOutputKeyClass(IntWritable.class);
        job.setOutputValueClass(IntWritable.class);
        // 3.4) output,指定job的输出
        Path outpath = new Path(args[1]);
        FileOutputFormat.setOutputPath(job,outpath);
        // 4) commit,执行job
        boolean isSuccess = job.waitForCompletion(true);
        // 如果正常执行返回0,否则返回1
        return (isSuccess) ? 0 : 1;
    }
    public static void main(String[] args) {
        // 添加输入,输入参数
        args = new String[]{
            "hdfs://bigdata-pro-m01:9000/user/caizhengjie/datas/2015082818",
            "hdfs://bigdata-pro-m01:9000/user/caizhengjie/mr/output"
        };
//        WordCountUpMR wordCountUpMR = new WordCountUpMR();
        Configuration configuration = new Configuration();
        try {
            // 判断输出的文件存不存在,如果存在就将它删除
            Path fileOutPath = new Path(args[1]);
            FileSystem fileSystem = FileSystem.get(configuration);
            if (fileSystem.exists(fileOutPath)){
                fileSystem.delete(fileOutPath,true);
            }
            // 调用run方法
            int status = ToolRunner.run(configuration,new WebPV(),args);
            // 退出程序
            System.exit(status);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

运行结果:

21.png22.png

注意:No reduce task的情况

当没有reduce任务的时候,combiner是不生效。但是,map端的shuffle过程是有的。


相关实践学习
【涂鸦即艺术】基于云应用开发平台CAP部署AI实时生图绘板
【涂鸦即艺术】基于云应用开发平台CAP部署AI实时生图绘板
相关文章
|
存储 运维 监控
SelectDB 实现日志高效存储与实时分析,完成任务可领取积分、餐具套装/水杯/帆布包!
SelectDB 实现日志高效存储与实时分析,完成任务可领取积分、餐具套装/水杯/帆布包!
|
SQL 监控 数据挖掘
SLS 重磅升级:超大规模数据实现完全精确分析
SLS 全新推出的「SQL 完全精确」模式,通过“限”与“换”的策略切换,在快速分析与精确计算之间实现平衡,满足用户对于超大数据规模分析结果精确的刚性需求。标志着其在超大规模日志数据分析领域再次迈出了重要的一步。
1108 118
|
11月前
|
监控 安全 搜索推荐
使用EventLog Analyzer进行日志取证分析
EventLog Analyzer助力企业通过集中采集、归档与分析系统日志及syslog,快速构建“数字犯罪现场”,精准追溯安全事件根源。其强大搜索功能可秒级定位入侵时间、人员与路径,生成合规与取证报表,确保日志安全防篡改,大幅提升调查效率,为执法提供有力证据支持。
396 0
|
监控 安全 NoSQL
【DevOps】Logstash详解:高效日志管理与分析工具
Logstash是ELK Stack核心组件之一,具备强大的日志收集、处理与转发能力。它支持多种数据来源,提供灵活的过滤、转换机制,并可通过插件扩展功能,广泛应用于系统日志分析、性能优化及安全合规等领域,是现代日志管理的关键工具。
1652 0
|
自然语言处理 监控 安全
阿里云发布可观测MCP!支持自然语言查询和分析多模态日志
阿里云可观测官方发布了Observable MCP Server,提供了一系列访问阿里云可观测各产品的工具能力,包含阿里云日志服务SLS、阿里云应用实时监控服务ARMS等,支持用户通过自然语言形式查询
2027 0
阿里云发布可观测MCP!支持自然语言查询和分析多模态日志
|
存储 消息中间件 缓存
MiniMax GenAI 可观测性分析 :基于阿里云 SelectDB 构建 PB 级别日志系统
基于阿里云SelectDB,MiniMax构建了覆盖国内及海外业务的日志可观测中台,总体数据规模超过数PB,日均新增日志写入量达数百TB。系统在P95分位查询场景下的响应时间小于3秒,峰值时刻实现了超过10GB/s的读写吞吐。通过存算分离、高压缩比算法和单副本热缓存等技术手段,MiniMax在优化性能的同时显著降低了建设成本,计算资源用量降低40%,热数据存储用量降低50%,为未来业务的高速发展和技术演进奠定了坚实基础。
750 1
MiniMax GenAI 可观测性分析 :基于阿里云 SelectDB 构建 PB 级别日志系统
|
人工智能 运维 监控
Aipy实战:分析apache2日志中的网站攻击痕迹
Apache2日志系统灵活且信息全面,但安全分析、实时分析和合规性审计存在较高技术门槛。为降低难度,可借助AI工具如aipy高效分析日志,快速发现攻击痕迹并提供反制措施。通过结合AI与学习技术知识,新手运维人员能更轻松掌握复杂日志分析任务,提升工作效率与技能水平。
|
SQL 关系型数据库 MySQL
MySQL事务日志-Undo Log工作原理分析
事务的持久性是交由Redo Log来保证,原子性则是交由Undo Log来保证。如果事务中的SQL执行到一半出现错误,需要把前面已经执行过的SQL撤销以达到原子性的目的,这个过程也叫做"回滚",所以Undo Log也叫回滚日志。
1011 7
MySQL事务日志-Undo Log工作原理分析
|
SQL 存储 自然语言处理
让跨 project 联查更轻松,SLS StoreView 查询和分析实践
让跨 project 联查更轻松,SLS StoreView 查询和分析实践
358 1
|
机器学习/深度学习 人工智能 运维
智能日志分析:用AI点亮运维的未来
智能日志分析:用AI点亮运维的未来
4912 15