MapReduce 分析 Youtube 数据

MapReduce 分析 Youtube 数据

一、实验介绍

1.1 实验内容

MapReduce 是 Hadoop 的计算框架。

在运行一个 MR 程序时,任务过程被分为两个阶段:Map 阶段和 Reduce 阶段,每个阶段都是用键值对作为输入和输出。

本课程的内容包括如何在 eclipse 中搭建 Hadoop 开发环境,并基于 YouTube 数据集根据任务需求编写 MR 程序,然后直接运行和生成 jar 包运行的区别等。

希望本课程能帮助您理解并学会搭建 Hadoop 开发环境和编写 MR 应用的方式。

1.2 先学课程

在学习本课程之前,建议您掌握 Hadoop 的基本使用技巧。以下是推荐的先学课程:

1.3 实验知识点

  • eclipse 的 Hadoop 开发环境搭建
  • 编写 MR 程序
  • 本地运行
  • 生成 Jar 包提交 yarn 运行(远程运行)

1.4 实验环境

  • Hadoop 2.6.1
  • eclipse indigo
  • Xfce 终端

1.5 适合人群

本课程属于中级难度级别,适合具有 Hadoop 基础的用户,如果对 MR 计算框架有所了解则能更好地上手本课程。

二、准备工作

2.1 数据集介绍及准备

本课程使用 Youtube 数据集,相关的介绍可以访问 Youtube Dataset 网站查看。目前,一小部分 Youtube 样例数据集整理好并已经上传到实验楼上。

该数据集各个字段的具体含义如下:

字段名解释及数据类型
video ID视频ID:每个视频均有唯一的11位字符串
uploader上传者用户名:字符串类型
age视频年龄:整数值,代表视频上传时间与2007年2月15日(Youtube创立日)的时间间隔
category分类:由上传者选择的视频分类,字符串类型
length视频长度:整数值
views浏览量:整数值
rate视频评分:浮点值
ratings评分次数:整数值
comments评论数:整数值
related IDs相关视频ID:数量不超过20个,每一个相关视频的ID均为单独的一列

由于数据集的字段有近20个,为了更直观的显示(一行数据仍旧没有显示完整),附图如下:

此处输入图片的描述

每条记录的相关视频数量不尽相同,因此数据不是很整齐。为了完成实验,我们需要过滤掉无效字段,稍后会给出项目详细代码注释。

您可以通过以下方式将数据集文件下载到实验环境中。

请双击打开桌面上的 Xfce 终端,然后输入以下命令:

本实验需要在 hadoop 用户下才能完成,请先切换到 hadoop 用户(命令已在下方提供)。密码为:hadoop

su - l hadoop 
wget http://labfile.oss.aliyuncs.com/courses/856/youtubedata.txt

此处输入图片的描述

使用 wc -l 命令查看数据行数。

wc -l youtubedata.txt

此处输入图片的描述

使用 head -n 1 命令查看数据的第一条记录。

head -n 1 youtubedata.txt

此处输入图片的描述

注意:以上仅是一条记录。

2.2 环境准备

由于要写 MapReduce 程序,必然要有 eclispe 下的 Hadoop 开发环境。我们又希望以生成 jar 包的方式提交和运行在 Hadoop 环境中,所以必须安装 Hadoop 。方便的是,实验楼环境里已经设置好了 Hadoop 的相关配置文件,我们只需要格式化 NameNode 即可使用。

双击打开桌面上的 Xfce 终端,按照以下步骤启动:

  1. 使用 su -l hadoop 命令切换到 hadoop 用户,hadoop 密码为 hadoop (如果已经切换则忽略此步骤)。
  2. 使用 hadoop namenode -format 命令对 NameNode 进行格式化。

此处输入图片的描述

以上命令输出信息太多,最终如下:

此处输入图片的描述

使用 start-all.sh 命令启动所有服务。

如果需要输入密码就输入 hadoop ,其他请根据提示进行输入。

此处输入图片的描述

使用 jps 命令可以查看到 NameNode、SecondaryNameNode、NodeManager、ResourceManager 和 DataNode 已启动。

此处输入图片的描述

三、项目需求描述E

根据已有数据集,编写 MapReduce 程序,分别实现以下功能:

  1. 从已经上传的视频中,找出评分前10的视频。

    示例如下("\t"分隔符隔开):

    CRVmdd_bpA    4.87
    BFBixmV-pc    4.81
    BArP_-_vXI    4.56
    6M41YqM_xk    4.52
    CYhSNHbeC8    4.3
    8BwggJEPZQ    4.29
    FDJ8x3ZKQE    3.75
    BbtF1Ysel0    3.4
    AdZ6wRHsrE    2.9
    AKiJZEOqzQ    1.33
    
  2. 从已经上传的视频中,分别统计每一个视频类型下的视频数量。

    示例如下("\t"分隔符隔开):

    Science & Technology    45
    Sports    23
    Travel & Events    144
    Nonprofits & Activism    44
    People & Blogs    35
    ...
    

四、项目实现

4.1 创建 Java Maven 项目

双击桌面上的图标打开 eclispe,关闭 Welcome 首页。

此处输入图片的描述

点击菜单栏中的 File -> New -> Other 。

此处输入图片的描述

选择 Maven Project 并点击 Next 按钮,在接下来的页面也点击 Next 按钮。

此处输入图片的描述

在 Select an Archetype 步骤时,选择 Artifact Id 为 maven-archetype-quickstart,并点击 Next 按钮。

此处输入图片的描述

分别输入 Group Id、Atifact Id 和 Package 的内容,并点击 Finish 按钮。

此处输入图片的描述

右下角会显示项目构建的进度,此过程取决于网速快慢,需要等待一些时间完成。

此处输入图片的描述

4.2 修改 pom.xml 文件,添加 Hadoop 依赖

在 pom.xml 文件中添加以下依赖项:

    <dependency>  
        <groupId>org.apache.hadoop</groupId>  
        <artifactId>hadoop-common</artifactId>  
        <version>2.6.1</version>  
    </dependency>  
    <dependency>  
        <groupId>org.apache.hadoop</groupId>  
        <artifactId>hadoop-hdfs</artifactId>  
        <version>2.6.1</version>  
    </dependency>  
    <dependency>  
        <groupId>org.apache.hadoop</groupId>  
        <artifactId>hadoop-client</artifactId>  
        <version>2.6.1</version>  
    </dependency>

形成的最终的 pom.xml 文件内容如下:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/
2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/
xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>shiyanlou</groupId>
    <artifactId>test</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>test</name>
    <url>http://maven.apache.org</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.apache.hadoop</groupId>  
            <artifactId>hadoop-common</artifactId>  
            <version>2.6.1</version>  
        </dependency>  
        <dependency>  
            <groupId>org.apache.hadoop</groupId>  
            <artifactId>hadoop-hdfs</artifactId>  
            <version>2.6.1</version>  
        </dependency>  
        <dependency>  
            <groupId>org.apache.hadoop</groupId>  
            <artifactId>hadoop-client</artifactId>  
            <version>2.6.1</version>  
        </dependency> 
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.1</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

此处输入图片的描述

编写完成后保存。随后会下载依赖,需要 10 分钟左右(取决于网速),请耐心等待完成。

4.3 编写代码

4.3.1 问题 1 :从已经上传的视频中,找出评分前 10 的视频

右键点击 src/main/java 目录下的 shiyanlou.test 包,并在弹出菜单中选择 New -> Class 。

此处输入图片的描述

输入包名 shiyanlou.test 和类名 RatingMR 并点击 Finish 按钮。

此处输入图片的描述

RatingMR.java 文件的全部代码如下:

package shiyanlou.test;

import org.apache.hadoop.conf.*;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.*;
import org.apache.hadoop.mapreduce.*;
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 java.io.IOException;

public class RatingMR {
    public static void main(String[] args) throws Exception {
        Configuration conf = new Configuration();

        @SuppressWarnings("deprecation")
        Job job = new Job(conf, "videorating");
        job.setJarByClass(RatingMR.class);

        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(FloatWritable.class);
        //设置 map ,reduce 类及 class
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(FloatWritable.class);

        job.setMapperClass(Map.class);
        job.setReducerClass(Reduce.class);
        //设置输入输出路径,这里指定的输路径是 HDFS,输出路径是 Linux 的本地文件系统
        job.setInputFormatClass(TextInputFormat.class);
        job.setOutputFormatClass(TextOutputFormat.class);
        FileInputFormat.addInputPath(job,
            new Path("hdfs://localhost:9000/youtubedata.txt"));
        FileOutputFormat.setOutputPath(job, new Path("/tmp/hadoop/out"));
        job.waitForCompletion(true);
    }

    //Map类,封装KV键值对 <k1,v1,k2,v2> , k1 是偏移量,v1 是这行数据,输出类型是这行文本 k2 ,浮点类型 v2
    public static class Map extends Mapper<LongWritable, Text, Text, FloatWritable> {
        //创建Text,FloatWritable 对象
        private Text video_name = new Text();
        private FloatWritable rating = new FloatWritable();

        public void map(LongWritable key, Text value, Context context)
            throws IOException, InterruptedException {
            String line = value.toString();
            String[] str = line.split("\t");

            //使用正则表达式匹配字符串只应包含浮点数,过滤掉小于 7 的无效字段
            if (str.length > 7) {
                video_name.set(str[0]);

                if (str[6].matches("\\d+.+")) {
                    //强制类型转换                    
                    float f = Float.parseFloat(str[6]);
                    rating.set(f);
                }
            }

            context.write(video_name, rating);
        }
    }

    public static class Reduce extends Reducer<Text, FloatWritable, Text, FloatWritable> {
        public void reduce(Text key, Iterable<FloatWritable> values,
            Context context) throws IOException, InterruptedException {
            float sum = 0;
            int l = 0;

            for (FloatWritable val : values) {
                l += 1;
                //累加value
                sum += val.get();
            }

            sum = sum / l;
            //取平均值
            context.write(key, new FloatWritable(sum));
        }
    }
}

在终端中输入以下命令,将 youtubedata.txt 上传到 HDFS 文件系统。

hadoop fs -put youtubedata.txt  hdfs://localhost:9000/

此处输入图片的描述

在 HDFS 文件系统中查看此文件是否上传成功。

hadoop fs -lsr hdfs://localhost:9000/

此处输入图片的描述

接下来运行程序,回到 eclipse ,在 src/main/Java 目录下新建一个 log4j.properties 文件用于配置 log4j 的日志输出格式。

注意:是在 src/main/Java 目录而不是 shiyanlou.test 包下面新建文件,请注意目录层级。

此处输入图片的描述

在新建文件对话框中选择 General -> File ,并点击 Next 按钮。

此处输入图片的描述

在 File name 文本框中填入文件名 log4j.properties ,并点击 Finish 按钮。

此处输入图片的描述

log4j.properties 文件中应填入的全部内容如下,该文件用于配置 log4j 输出的日志格式:

log4j.rootLogger=INFO, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n
log4j.appender.logfile=org.apache.log4j.FileAppender
log4j.appender.logfile.File=target/spring.log
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - %m%n

随后,右键点击 RatingMR.java,在弹出的菜单中选择 Run As -> Java Application 。

此处输入图片的描述

在 eclipse 的 console 中查看到任务已经执行完毕后,返回终端查看是否有输出结果。

此处输入图片的描述

注意:/tmp/hadoop/out 目录不需要手动创建,运行 MR 程序会自动产生。

从上图看以看到,用 head 命令只显示了前20行,但其实里面包含了所有的评分数据。那怎样才能获得我们想要的结果呢?我们可以结合 Linux 命令进行获取。

请在终端中输入以下命令。

hadoop fs -cat file:///tmp/hadoop/out/part-r-00000 | sort -n -k 2 -r | head -n 10

此处输入图片的描述

注意:file:///xxx 指定的是本地的 Linux 文件系统。如果不加 file:///,默认会去 HDFS 上去找,而我们在项目代码里指定 Linux 文件系统时则不需要加 file:/// ,因为只有 HDFS 上不存在指定文件时,才会去本地找。

如结果所示,获得了我们需要的结果,即项目提出的问题 1 得以解决。

4.3.2 问题 2 :从已经上传的视频中,分别统计每一个视频类型下的视频数量

右键点击 shiyanlou.test 包,在弹出的菜单中选择 New -> Class 。

此处输入图片的描述

输入类名 VideoCount 并点击 Finish 按钮。

此处输入图片的描述

VideoCount.java 文件的全部代码如下:

package shiyanlou.test;

import org.apache.hadoop.conf.*;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.*;
import org.apache.hadoop.mapreduce.*;
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 java.io.IOException;


public class VideoCount {
    //主函数
    public static void main(String[] args) throws Exception {
        Configuration conf = new Configuration();

        @SuppressWarnings("deprecation")
        Job job = new Job(conf, "categories");
        //设置生产 jar 包所使用的类
        job.setJarByClass(VideoCount.class);
        //设置 Map 类的输入输出类型
        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(IntWritable.class);
        //设置 Reduce 类的输入输出类型    
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(IntWritable.class);
        //设置 Map, Reduce 类的 class 参数
        job.setMapperClass(Map.class);
        job.setReducerClass(Reduce.class);
        //指定格式化用的类型
        job.setInputFormatClass(TextInputFormat.class);
        job.setOutputFormatClass(TextOutputFormat.class);
        FileInputFormat.addInputPath(job,
            new Path("hdfs://localhost:9000/youtubedata.txt"));
        FileOutputFormat.setOutputPath(job, new Path("/tmp/hadoop/out2"));
        //等待完成
        job.waitForCompletion(true);
    }

    public static class Map extends Mapper<LongWritable, Text, Text, IntWritable> {
        private final static IntWritable one = new IntWritable(1);

        //构造文本类Text对象、IntWritable 对象,也可以直接以匿名函数的方式创建
        private Text tx = new Text();

        //map 的逻辑,使用tab“\ t”分隔符来分割行,并将值存储在String Array中,以使一行中的所有列都存储在字符串数组中
        public void map(LongWritable key, Text value, Context context)
            throws IOException, InterruptedException {
            //拿到每一行数据
            String line = value.toString();
            String[] str = line.split("\t");

            //过滤字段
            if (str.length > 5) {
                tx.set(str[3]);
            }

            //输出key,value
            context.write(tx, one);
        }
    }

    //编写 reduce,接收 map 阶段传来的 kv 键值对,输出的类型和传进来的类型一致
    public static class Reduce extends Reducer<Text, IntWritable, Text, IntWritable> {
        //reduce????
        public void reduce(Text key, Iterable<IntWritable> values,
            Context context) throws IOException, InterruptedException {
            int sum = 0;

            //累加求和评分
            for (IntWritable v : values) {
                sum += v.get()
            }

            //写出去
            context.write(key, new IntWritable(sum));
        }
    }
}

我们已经上传 youtubedata.txt 到 HDFS 文件系统,因此这里就不需要上传可以直接使用。

仍然是在终端中输入命令,查看 HDFS 中的文件是否上传成功。

hadoop fs -lsr hdfs://localhost:9000/

此处输入图片的描述

接下来运行程序,右键点击 VideoCount.java,在弹出的菜单中选择 Run As -> Java Application 。

此处输入图片的描述

返回终端,查看是否有输出结果。

cd /tmp/hadoop/out2/
ls

此处输入图片的描述

注意:/tmp/hadoop/out2 目录不需要手动创建,运行 MR 程序时会自动生成。

我们使用相关的 Linux 命令来查看所有数据:

cat part-r-00000

此处输入图片的描述

如上图所示,我们获得了需要的结果,即项目提出的问题 2 得以解决。

4.3.3 知识拓展

如果你是在 Windows 平台写的代码,则可以用通过生成 jar 包的方式提交运行,具体步骤如下:

右键点击项目 test,在弹出的菜单中选择 Export 。

此处输入图片的描述

选择 Java -> JAR file 并点击 Next 按钮。

此处输入图片的描述

输入 /tmp/hadoop/youtube.jar 并点击 Finish 按钮。

此处输入图片的描述

进到终端,查看刚才生成的jar包

此处输入图片的描述

这里我们用另外一种方式,即 shell 方式提交和运行程序,不同于 eclispe 。以生成 jar 包的形式提交 hadoop yarn 上运行,语法如下:

hadoop jar 生成的jar包路径 全类名 输入路径input 输出路径output。

由于我们在 eclispe 代码里面输入路径,输出路径已经写死。

FileInputFormat.addInputPath(job, new Path("hdfs://localhost:9000/youtubedata.txt"));
FileOutputFormat.setOutputPath(job, new Path("/tmp/hadoop/out2"));

因此,在提交的时候就可以省略后两项参数。这里我们仍然补齐,但是在这里可以做个改动,尝试将输出路径改为其它目录(hdfs://localhost:9000/output),并且不提前创建,看是否会报错。

在提交之前,为了避免遇到 Hadoop 经由 Namenode 抛出的 SafeModeException 异常,我们需要先关闭 HDFS 的安全模式。

请在终端中输入以下命令:

hdfs dfsadmin -safemode leave

此处输入图片的描述

接着,就可以将 jar 包提交运行了。

hadoop jar /tmp/hadoop/youtube.jar shiyanlou.test.VideoCount hdfs://localhost:9000/youtubedata.txt hdfs://localhost:9000/output

此处输入图片的描述

稍作等候,提示 Job 运行成功后,在终端输入如下命令:

hadoop fs -lsr hdfs://localhost:9000/output/

此处输入图片的描述

你可能会发现:为什么任务执行成功却找不到文件呢?这是因为在 eclipse 的项目中已经写死路径了,故在命令行执行更换目录的操作并未生效。之前在 eclispe 里面直接运行任务的时候,输出的结果在 Linux 的本地文件系统中。那么这次的输出结果在哪里呢?

可以尝试在终端中使用 HDFS 的操作命令查找,或在 HDFS Web UI 中查找。

经过查找可以发现输出在 HDFS ,使用以下命令查看:

hadoop fs -cat hdfs://localhost:9000/tmp/hadoop/out2/*

此处输入图片的描述

可以发现,此处与 eclispe 直接执行的结果完全相同,只是输出路径不同。用 Hadoop 命令执行任务时,输出路径指向 HDFS 。

接下来访问 Hadoop 的 8088 端口来查看任务情况。

双击打开桌面的 Firefox 网络浏览器,输入 localhost:8088 ,然后回车。

此处输入图片的描述

由于作者在此处提交执行了 3 次,故显示 Application jobs 为 3 。该界面中的信息较多,你还可以尝试使用其他更多的功能。

至此,本课程的实验部分就结束了。

五、实验总结

本课程主要介绍了 Hadoop 开发环境的搭建过程,并基于 YouTube 数据集根据任务需求编写 MR 程序,然后讲解演示了直接运行与生成 jar 包运行 Hadoop 任务的区别等。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值