Hadoop(4-1)-MapReduce程序案例-统计销售商品数量

1.问题陈述:
找出销往各个国家商品数量。
输入: 我们的畋输入数据集合是一个 CSV 文件, Sales2014.csv
输出:国家名 销往次国家的商品数量
商品信息如图所示
Hadoop(4-1)-MapReduce程序案例-统计销售商品数量_第1张图片
2.前提条件:
• 本教程是在Linux上开发
• 已经安装了Hadoop(本教程使用版本2.6.5)
• 系统上已安装了Java(本教程使用 JDK1.8.0)。
3.步骤:(先运行成功再看4的解释)
3.1.创建一个新的目录名称是:MapReduceTutorial
[root@hdp-node-01 /]# cd /usr/local
[root@hdp-node-01 local]# mkdir MapReduceTutorial
3.2.授予权限
[root@hdp-node-01 local]# chmod -R 777 MapReduceTutorial
3.3 下载相关文件:下载 Java 程序文件,拷贝以下文件:SalesMapper.java, SalesCountryReducer.java 和 SalesCountryDriver.java 到 MapReduceTutorial 目录中,
3.4 检查所有这些文件的文件权限是否正确:ll
Hadoop(4-1)-MapReduce程序案例-统计销售商品数量_第2张图片
如果“读取”权限缺少可重新再授予权限,执行以下命令: chmod +r *
3.5 在MapReduceTutorial文件夹下新建一个文件夹SalesCountry用于存放编译好的class文件
[root@hdp-node-01 MapReduceTutorial]# mkdir SalesCountry
3.6.导出类路径

[root@hdp-node-01 MapReduceTutorial]# export CLASSPATH="$HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-client-core-2.6.5.jar:$HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-client-common-2.6.5.jar:$HADOOP_HOME/share/hadoop/common/hadoop-common-2.6.5.jar:~/MapReduceTutorial/SalesCountry/*:$HADOOP_HOME/lib/*"

注意:其中Hadoop版本需替换为自己的
执行完此步后java编译后将放在SalesCountry文件夹下
3.7 编译Jav[root@hdp-node-01 MapReduceTutorial]# javac -d . SalesMapper.java SalesCountryReducer.java SalesCountryDriver.java上面-d后有一点

3.8查看SalesCountry文件夹

[root@hdp-node-01 MapReduceTutorial]# ls ./SalesCountry

SalesCountryDriver.class SalesCountryReducer.class SalesMapper.class

3.9 创建一个新的文件:Manifest.txt——–这样运行jar时不用再指定主类
[root@hdp-node-01 MapReduceTutorial]# vi Manifest.txt
添加以下内容到文件中:
Main-Class: SalesCountry.SalesCountryDriver
其中SalesCountry.SalesCountryDriver 是主类的名称。请注意,必须键入回车键,在该行的末尾。
3.10 创建一个 jar 文件

[root@hdp-node-01 MapReduceTutorial]# jar cfm ProductSalePerCountry.jar Manifest.txt SalesCountry/*.class

注:前提需配置了Java环境变量
检查所创建的 jar 文件,结果如下:
Hadoop(4-1)-MapReduce程序案例-统计销售商品数量_第3张图片

3.11 启动 Hadoop

[root@hdp-node-01 MapReduceTutorial]#$HADOOP_HOME/sbin/start-dfs.sh
[root@hdp-node-01 MapReduceTutorial]#$HADOOP_HOME/sbin/start-yarn.sh

3.12 传到HDFS

[root@hdp-node-01 MapReduceTutorial]# hdfs dfs -mkdir /inputMapReduce
 [root@hdp-node-01 MapReduceTutorial]# hdfs dfs -put ./Sales2014.csv /inputMapReduce
[root@hdp-node-01 MapReduceTutorial]# hadoop fs -ls /inputMapReduce

3.13. 运行MapReduce 作业

[root@hdp-node-01 MapReduceTutorial]# hadoop jar ProductSalePerCountry.jar   /inputMapReduce  /mapreduce_output_sales

SalesCountry.SalesCountryDriver :主类
inputMapReduce :输入目录
mapreduce_output_sales: 程序将在HDFS上自动创建一个输出目录。
Hadoop(4-1)-MapReduce程序案例-统计销售商品数量_第4张图片

若没有成功配置Manifest指定主类打包jar,则需加上主类
[root@hdp-node-01 MapReduceTutorial]# hadoop jar ProductSalePerCountry.jar SalesCountry.SalesCountryDriver /inputMapReduce /mapreduce_output_sales
3.14. 结果可以通过命令界面中可以看到

[root@hdp-node-01 MapReduceTutorial]# hdfs dfs -cat /mapreduce_output_sales/part-00000

Hadoop(4-1)-MapReduce程序案例-统计销售商品数量_第5张图片

结果也可以通过 Web 界面看到,打开 Web 浏览器,输入网址:http://192.168.33.101:50070/dfshealth.jsp ,结果如下:

Hadoop(4-1)-MapReduce程序案例-统计销售商品数量_第6张图片

现在选择 ‘Browse the filesystem’ 并导航到 /mapreduce_output_sales 如下:
Hadoop(4-1)-MapReduce程序案例-统计销售商品数量_第7张图片
打开 part-r-00000 ,如下图所示:

下载后,查看结果内容。
3.15 重新运行需删除输出目录:

[root@hdp-node-01 MapReduceTutorial]# hdfs dfs -rmr /mapreduce_output_sales/

4.程序说明
4.1 SalesMapper.java

package SalesCountry;

import java.io.IOException;

import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapred.*;
//每一个 Mapper可以选择性地继承 MapReduceBase ,它必须实现 Mapper 接口。
//Mapper
public class SalesMapper extends MapReduceBase implements Mapper<LongWritable, Text, Text, IntWritable> {
        private final static IntWritable one = new IntWritable(1);
        public void map(LongWritable key, Text value, OutputCollector output, Reporter reporter) throws IOException {
        String valueString = value.toString();
        //这里,“,” 被用作分隔符。
        String[] SingleCountryData = valueString.split(",");
        //在这之后,使用记录在数组  'SingleCountryData' 中的第七索引,其值为 '1'.
        output.collect(new Text(SingleCountryData[7]), one);
        }
}

注:
在这之后,使用记录在数组 ‘SingleCountryData’ 中的第七索引,其值为 ‘1’.
output.collect(new Text(SingleCountryData[7]), one);
我们在选择第7索引记录,因为我们需要的国家数据,它位于数组 ‘SingleCountryData’ 的第七索引。
请注意,我们输入的数据是下面的格式 (Country 在索引的位置为:7, 0 是开始的索引)-
Transaction_date,Product,Price,Payment_Type,Name,City,State,Country,Account_Created,Last_Login,Latitude,Longitude
Mapper的输出使用的是 ‘OutputCollector’ 的 ‘collect()’ 方法的键值对.

Hadoop(4-1)-MapReduce程序案例-统计销售商品数量_第8张图片
4.2 SalesCountryReducer.java

package SalesCountry;

import java.io.IOException;
import java.util.*;

import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapred.*;
    //嵌套类Reducer
    //Reduce
    //Reducer的valuein类型要和Mapper的valueout类型一致,
    // Reducer的valuein是Mapper的valueout经过shuffle之后的值

public class SalesCountryReducer extends MapReduceBase implements Reducer<Text, IntWritable, Text, IntWritable> {

        public void reduce(Text t_key, Iterator values, OutputCollector output, Reporter reporter) throws IOException {
                Text key = t_key;
                int frequencyForCountry = 0;
                while (values.hasNext()) {
                        // replace type of value with the actual type of our value
                        IntWritable value = (IntWritable) values.next();
                        frequencyForCountry += value.get();

                }
                output.collect(key, new IntWritable(frequencyForCountry));
        }
}

注:
输入到 reduce() 方法是在具有多个值的列表中选择一个键。
例如,在我们的示例中,这将是 -
< United Arab Emirates, 1>, < United Arab Emirates, 1>, < United Arab Emirates, 1>,< United Arab Emirates, 1>, < United Arab Emirates, 1>, < United Arab Emirates, 1>.
这赋予 reducer 作为 < United Arab Emirates, {1,1,1,1,1,1}>
因此,接受这种形式参数,前两个数据类型的使用,即 Text 和 Iterator< IntWritable>.
Text是一个数据类型的键 和,Iterator< IntWritable>为对于键的值的列表的数据类型。
接下来的参数的类型是 OutputCollector< Text,IntWritable> 它收集 reducer 阶段的输出。
reduce() 方法开始通过复制键值和初始化频率计数为0。
Text key = t_key;
int frequencyForCountry = 0;
然后,使用 “while” 循环,我们通过与键关联的值列表循环,并通过总结所有计算的值。
while(values.hasNext()) {
// replace type of value with the actual type of our value
IntWritable value = (IntWritable) values.next();
frequencyForCountry += value.get();
}
现在,结果中的键得到的频率计数输出到收集器。
下面的代码执行这个 -output.collect(key, new IntWritable(frequencyForCountry));

4.3 SalesCountryDriver.java

package SalesCountry;

import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.*;
import org.apache.hadoop.mapred.*;

public class SalesCountryDriver {
        public static void main(String[] args) {
                JobClient my_client = new JobClient();
                // Create a configuration object for the job
                JobConf job_conf = new JobConf(SalesCountryDriver.class);

        // Set a name of the Job
        job_conf.setJobName("SalePerCountry");

        // Specify data type of output key and value
        job_conf.setOutputKeyClass(Text.class);
        job_conf.setOutputValueClass(IntWritable.class);

        // Specify names of Mapper and Reducer Class
        job_conf.setMapperClass(SalesCountry.SalesMapper.class);
        job_conf.setReducerClass(SalesCountry.SalesCountryReducer.class);

        // Specify formats of the data type of Input and output
        job_conf.setInputFormat(TextInputFormat.class);
        job_conf.setOutputFormat(TextOutputFormat.class);

        // Set input and output directories using command line arguments, 
        //arg[0] = name of input directory on HDFS, and arg[1] =  name of output directory to be created to store the output file.

        FileInputFormat.setInputPaths(job_conf, new Path(args[0]));
        FileOutputFormat.setOutputPath(job_conf, new Path(args[1]));

        my_client.setConf(job_conf);
        try {
                // Run the job 
                JobClient.runJob(job_conf);
        } catch (Exception e) {
                e.printStackTrace();
        }
        }
}

注:
1 定义一个用于创建一个新的客户端工作,配置 Mapper及Reducer 类对象驱动程序类。该驱动程序类负责设置我们的 MapReduce 作业在 Hadoop 运行。 在这个类中,我们指定作业名称,输入/输出,mapper 和 reducer 类名称的数据类型。

2 在下面的代码片段中,我们设置这是用来输入数据集消费和生产输出,分别输入和输出目录。
arg[0] 和 arg[1] 是通过 MapReduce 的实际操作,也就是赋予在命令行参数执行命令

[root@hdp-node-01 MapReduceTutorial]# hadoop jar ProductSalePerCountry.jar   /inputMapReduce  /mapreduce_output_sales

3 触发我们的作业
下面的代码开始执行 MapReduce 作业
try{
// Run the job
JobClient.runJob(job_conf);
} catch(Exception e) {
e.printStackTrace();
}

你可能感兴趣的:(Hadoop)