本教程中的代码分为 3 个部分:
SalesMapper类的说明
在本节中,我们将了解 SalesMapper 类的实现。
我们首先指定类的包名称。 SalesCountry 就是这个示例中使用的包名。请注意编译的输出,SalesMapper.class 将进入目录并命名这个软件包名称:SalesCountry.
其次,我们导入库软件包。
以下快照显示实现 SalesMapper 类
代码解释:
1. SalesMapper 类定义
public class SalesMapper extends MapReduceBase implements Mapper<LongWritable, Text, Text, IntWritable> {...}
每一个 mapper 类必须从 MapReduceBase 类进行扩展,它必须实现 Mapper 接口。
2. 定义 'map' 函数
1
2
3
4
|
publicvoidmap(LongWritable key,
Text value,
OutputCollector<Text, IntWritable> output,
Reporter reporter) throwsIOException
|
Mapper类的主要部分是接受四个参数的 “map()” 方法。
每次调用 'map()' 方法, 一个键值 key-value 对 ('key' 和 'value' 在代码里) 被传递。
'map()' 方法开始被接受拆分输入文本作为一个参数,并使用分词来拆分这些行成词。
1
2
|
String valueString = value.toString();
String[] SingleCountryData = valueString.split(",");
|
这里,“,” 被用作分隔符。
在这之后,使用记录在数组 '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()' 方法的键值对.
SalesCountryReducer 类的说明
在本节中,我们将了解 SalesCountryReducer 类的实现。
1. 我们首先为类指定包的名称。SalesCountry 是包的名称。请注意编译的输出, SalesCountryReducer.class 将进入命名这个软件包名称目录: SalesCountry.
其次,我们导入库软件包。
以下快照显示实现 SalesCountryReducer 类
代码解释:
1. SalesCountryReducer 类定义 -
public class SalesCountryReducer extends MapReduceBase implements Reducer<Text, IntWritable, Text, IntWritable> {
此处,前两个数据类型, 'Text' 和 'IntWritable' 是输入键值的数据类型到reducer。
映射器的输出的形式<CountryName1, 1>, <CountryName2, 1>.映射器的输出被输入到reducer。所以,以配合其数据类型, Text 和 IntWritable 数据在这里输入被使用。
最后两个数据类型,'Text' 和 'IntWritable' 是由 reducer 的键 - 值对的形式生成的输出的数据类型。
每个 reducer 类必须从MapReduceBase类进行扩展,它必须实现 Reducer 接口。
2. Defining 'reduce' function-
1
2
3
|
publicvoidreduce( Text t_key,
Iterator<IntWritable> values, OutputCollector<Text,IntWritable> output,
Reporter reporter) throwsIOException {
|
输入到 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” 循环,我们通过与键关联的值列表循环,并通过总结所有计算的值。
1
2
3
4
5
6
|
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));
SalesCountryDriver类的说明
在本节中,我们将了解 SalesCountryDriver 类实现。
1. 我们首先为类指定包的名称。 SalesCountry 是这里使用的包名。请注意编译的输出, SalesCountryDriver.class 将进入命名这个包名称的目录: SalesCountry.
这里一行指定是包名称后面的代码是导入库软件包。
2. 定义一个用于创建一个新的客户端工作,配置 Mapper及Reducer 类对象驱动程序类。
该驱动程序类负责设置我们的 MapReduce 作业在 Hadoop 运行。 在这个类中,我们指定作业名称,输入/输出,mapper 和 reducer 类名称的数据类型。
3. 在下面的代码片段中,我们设置这是用来输入数据集消费和生产输出,分别输入和输出目录。
arg[0] 和 arg[1] 是通过 MapReduce 的实际操作,也就是赋予在命令行参数执行命令,
$HADOOP_HOME/bin/hadoop jar ProductSalePerCountry.jar /inputMapReduce /mapreduce_output_sales
4. 触发我们的作业
下面的代码开始执行 MapReduce 作业
try{ // Run the job JobClient.runJob(job_conf); } catch(Exception e) { e.printStackTrace(); }
在MapReduce的计数器是用于收集关于 MapReduce 工作的统计信息的机制。这个信息在MapReduce的作业处理的问题的诊断是很有用的。 计数器类似于将在 map 或 reduce 在代码日志信息中。
通常情况下,这些计数器在一个程序(map 或 reduce)中定义,当一个特定事件或条件(特定于该计数器)发生执行期间递增。计数器是一个很好的应用来从输入数据集跟踪有效和无效的记录。
有两种类型的计数器:
1. Hadoop 内置计数器: 有一些内置计数器存在每个作业中。下面是内置计数器组:
- MapReduce任务计数器 - 收集任务的具体信息(例如,输入记录的数量)在它的执行期间。
- 文件系统计数器 - 收集信息像由一个任务读取或写入的字节数
- FileInputFormat计数器 - 收集通过FileInputFormat读取的字节数的信息
- FileOutputFormat计数器 - 收集的字节数量的信息通过 FileOutputFormat 写入
- Job 计数器- 这些计数器使用 JobTracker。它们收集统计数据包括如,任务发起了作业的数量。
2. 用户定义的计数器
除了内置的计数器,用户可以定义自己的计数器,通过使用编程语言提供了类似的功能。 例如,在 Java 的枚举用于定义用户定义的计数器。
一个MapClass例子使用计数器计算缺失和无效值的数量:
|
publicstaticclassMapClass
extendsMapReduceBase
implementsMapper<LongWritable, Text, Text, Text>
{
staticenumSalesCounters { MISSING, INVALID };
publicvoidmap ( LongWritable key, Text value,
OutputCollector<Text, Text> output,
Reporter reporter) throwsIOException
{
//Input string is split using ',' and stored in 'fields' array
String fields[] = value.toString().split(",", -20);
//Value at 4th index is country. It is stored in 'country' variable
String country = fields[4];
//Value at 8th index is sales data. It is stored in 'sales' variable
String sales = fields[8];
if(country.length() == 0) {
reporter.incrCounter(SalesCounters.MISSING, 1);
} elseif(sales.startsWith("\"")) {
reporter.incrCounter(SalesCounters.INVALID, 1);
} else{
output.collect(newText(country), newText(sales + ",1"));
}
}
}
|
上面的代码片段显示在 Map Reduce 实现计数器的示例。
在这里,SalesCounters是用“枚举”定义的计数器。它被用来计算 MISSING 和 INVALID 的输入记录。
在代码段中,如果 “country” 字段的长度为零那么它的值丢失,因此相应的计数器 SalesCounters.MISSING 递增。
接下来,如果 “sales” 字段开头是符号 '' ,则记录被视为无效。这通过递增计数器 SalesCounters.INVALID 来表示。
MapReduce 连接
连接两个大的数据集可以使用 MapReduce Join 来实现。然而,这个过程需要编写大量的代码来执行实际的连接操作。
连接两个数据集开始是通过比较每个数据集的大小。如果因为相比其他数据集一个数据集小,那么小数据集被分布到集群中的每个数据节点。一旦分散,无论是 Mapper 或 Reducer 使用更小的数据集进行查找匹配的大型数据集的记录,然后结合这些记录,形成输出记录。
这取决于在实际连接进行的地方,这个连接分为:
1. 映射端连接 - 当该联接是由映射器执行的,它称为映射端链接。在这种类型中,联结前的数据由映射函数实际来消耗的处理。它是强制性的,输入到每个映射是在分区中的形式,并且是按排序顺序。另外,必须有一个相等数目的分区,它必须由连接键进行排序。
2. Reduce端连接- 当连接是通过减速器进行的,称为reduce端连接。没有必要在此连接有数据集中在以结构化形式(或分区)。
在这里,映射端的处理发出连接这两个表的关键字和对应的元组。作为该处理的效果,所有的元组相同连接键都落在相同的 reducer,然后使用相同的连接键连接记录。
整体处理流程示于下图。
这里有两个数据集合在两个不同的文件中,如下所示:
DEPT_ID 键在这两个文件中常见的。
目标是使用 MapReduce 加入来组合这些文件。
输入: 我们的输入数据集是两个txt文件:DeptName.txt 和 DepStrength.txt
前提条件:
- 本教程是在 Linux 上开发 - Ubuntu操作系统
- 已经安装的Hadoop(本教程使用2.7.1版本)
- Java的开发运行环境已经在系统上安装(本教程使用的版本是:1.8.0)
在我们开始实际操作之前,使用的用户 'hduser_'(使用 Hadoop 的用户)。
yiibai@ubuntu:~$ su hduser_
步骤
Step 1) 复制 zip 文件到您选择的位置
hduser_@ubuntu:/home/yiibai$ cp /home/yiibai/Downloads/MapReduceJoin.tar.gz /home/hduser_/ hduser_@ubuntu:/home/yiibai$ ls /home/hduser_/
操作过程及结果如下:
Step 2) 解压缩ZIP文件,使用以下命令:
hduser_@ubuntu:~$ sudo tar -xvf MapReduceJoin.tar.gz
Step 3) 进入目录 MapReduceJoin/
hduser_@ubuntu:~$ cd MapReduceJoin/
Step 4) 启动 Hadoop
hduser_@ubuntu:~/MapReduceJoin$ $HADOOP_HOME/sbin/start-dfs.sh hduser_@ubuntu:~/MapReduceJoin$ $HADOOP_HOME/sbin/start-yarn.sh
Step 5) DeptStrength.txt 和 DeptName.txt 用于此项目的输入文件
这些文件需要使用以下命令 - 复制到 HDFS 的根目录下,使用以下命令:
hduser_@ubuntu:~/MapReduceJoin$ $HADOOP_HOME/bin/hdfs dfs -copyFromLocal DeptStrength.txt DeptName.txt /
Step 6) 使用以下命令 - 运行程序
hduser_@ubuntu:~/MapReduceJoin$ $HADOOP_HOME/bin/hadoop jar MapReduceJoin.jar /DeptStrength.txt /DeptName.txt /output_mapreducejoin
Step 7)
在执行命令后, 输出文件 (named 'part-00000') 将会存储在 HDFS目录 /output_mapreducejoin
结果可以使用命令行界面可以看到:
hduser_@ubuntu:~/MapReduceJoin$ $HADOOP_HOME/bin/hdfs dfs -cat /output_mapreducejoin/part-00000
结果也可以通过 Web 界面查看(这里我的虚拟机的IP是 192.168.1.109),如下图所示:
现在,选择 “Browse the filesystem”,并浏览到 /output_mapreducejoin
打开 part-r-00000
结果如下所示,点击 Download 链接下载:
打开下载后的 文件,结果如下所示:
注:请注意,下一次运行此程序之前,需要删除输出目录 /output_mapreducejoin
$HADOOP_HOME/bin/hdfs dfs -rm -r /output_mapreducejoin
另一种方法是使用不同的名称作为输出目录。
from: http://www.yiibai.com/hadoop/