一、实验目的
-
通过实验掌握基本的MapReduce编程方法;
-
掌握用MapReduce解决一些常见的数据处理问题,编写计数器程序。
二、实验平台
-
操作系统:Linux(建议CentOS6.5);
-
Hadoop版本:2.9.2;
-
JDK版本:1.8或以上版本;
-
Java IDE:Eclipse;
三、实验要求
能够理解MapReduce编程思想,然后会编写MapReduce版本计数器程序,并能执行该程序和分析执行过程。
四、实验背景
1、MapReduce计数器是什么?
计数器是用来记录job的执行进度和状态的。它的作用可以理解为日志。我们可以在程序的某个位置插入计数器,记录数据或者进度的变化情况。
2、MapReduce计数器能做什么?
MapReduce 计数器(Counter)为我们提供一个窗口,用于观察 MapReduce Job 运行期的各种细节数据。对MapReduce性能调优很有帮助,MapReduce性能优化的评估大部分都是基于这些 Counter 的数值表现出来的。
3、内置计数器
MapReduce 自带了许多默认Counter,现在我们来分析这些默认 Counter 的含义,方便大家观察 Job 结果,如输入的字节数、输出的字节数、Map端输入/输出的字节数和条数、Reduce端的输入/输出的字节数和条数等。下面我们只需了解这些内置计数器,知道计数器组名称(groupName)和计数器名称(counterName),以后使用计数器会查找groupName和counterName即可。
4、计数器使用
(1)定义计数器
枚举声明计数器:
// 自定义枚举变量Enum
Counter counter = context.getCounter(Enum enum)
自定义计数器:
// 自己命名groupName和counterName
Counter counter = context.getCounter(String groupName,String counterName)
(2)为计数器赋值
初始化计数器:
counter.setValue(long value);//设置初始值
计数器自增:
counter.increment(long incr);// 增加计数
(3)获取计数器的值
获取枚举计数器的值:
Configuration conf = new Configuration();
Job job = new Job(conf, "MyCounter");
job.waitForCompletion(true);
Counters counters=job.getCounters();
Counter counter=counters.findCounter(LOG_PROCESSOR_COUNTER.BAD_RECORDS_LONG);
//获取自定义计数器的值:
long value=counter.getValue();
Configuration conf = new Configuration();
Job job = new Job(conf, "MyCounter");
job.waitForCompletion(true);
Counters counters = job.getCounters();
Counter counter=counters.findCounter("ErrorCounter","toolong");// 假如groupName为ErrorCounter,counterName为toolong
//获取内置计数器的值:
long value = counter.getValue();
Configuration conf = new Configuration();
Job job = new Job(conf, "MyCounter");
job.waitForCompletion(true);
Counters counters=job.getCounters();
Counter counter=counters.findCounter("org.apache.hadoop.mapreduce.JobCounter","TOTAL_LAUNCHED_REDUCES") ;
long value=counter.getValue();
Configuration conf = new Configuration();
Job job = new Job(conf, "MyCounter");
Counters counters = job.getCounters();
for (CounterGroup group : counters) {
for (Counter counter : group) {
System.out.println(counter.getDisplayName() + ": " + counter.getName() + ": "+ counter.getValue());
}
}
(5)自定义计数器
MapReduce允许用户编写程序来定义计数器,计数器的值可在mapper或reducer 中增加。多个计数器由一个Java枚举(enum)类型来定义,以便对计数器分组。一个作业可以定义的枚举类型数量不限,各个枚举类型所包含的字段数量也不限。枚 举类型的名称即为组的名称,枚举类型的字段就是计数器名称。计数器是全局的。换言之,MapReduce框架将跨所有map和reduce聚集这些计数器,并在作业结束 时产生一个最终结果。
五、实验步骤
1、实验分析设计
该实验要求学生自己实现一个计数器,统计输入的无效数据。说明如下:假如一个文件,规范的格式是3个字段,“\t”作为分隔符,其中有2条异常数据,一条数据是只有2个字段,一条数据是有4个字段。其内容如下所示:
jim 1 28
kate 0 26
tom 1
lily 0 29 22
编写代码统计文档中字段不为3个的异常数据个数。如果字段超过3个视为过长字段,字段少于3个视为过短字段。
2、编写程序代码:
package mr ;
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Counter;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.util.GenericOptionsParser;
public class Counters {
public static class MyCounterMap extends Mapper<LongWritable, Text, Text, Text> {
public static Counter ct = null;
protected void map(LongWritable key, Text value,
org.apache.hadoop.mapreduce.Mapper<LongWritable, Text, Text, Text>.Context context)
throws java.io.IOException, InterruptedException {
String arr_value[] = value.toString().split("\t");
if (arr_value.length < 3) {
ct = context.getCounter("ErrorCounter", "toolong"); // ErrorCounter为组名,toolong为组员名
ct.increment(1); // 计数器加一
} else if (arr_value.length>=3) {
ct = context.getCounter("ErrorCounter", "tooshort");
ct.increment(1);
}
}
}
public static void main(String[] args) throws IOException, InterruptedException, ClassNotFoundException {
Configuration conf = new Configuration();
String[] otherArgs = new GenericOptionsParser(conf, args).getRemainingArgs();
if (otherArgs.length != 2) {
System.err.println("Usage: Counters <in> <out>");
System.exit(2);
}
Job job = new Job(conf, "Counter");
job.setJarByClass(Counters.class);
job.setMapperClass(MyCounterMap.class);
FileInputFormat.addInputPath(job, new Path(otherArgs[0]));
FileOutputFormat.setOutputPath(job, new Path(otherArgs[1]));
System.exit(job.waitForCompletion(true) ? 0 : 1);
}
}
3、打包并提交
使用Eclipse开发工具将该代码打包,选择主类为mr.Counters。假定打包后的文件名为Counters.jar,主类Counters位于包mr下,则可使用如下命令向Hadoop集群提交本应用。
[root@master hadoop]# bin/hadoop jar Counters.jar mr.Counters /usr/counters/in/counters.txt /usr/counters/out
其中“hadoop”为命令,“jar”为命令参数,后面紧跟打包。 “/usr/counts/in/counts.txt”为输入文件在HDFS中的位置(如果没有,自行上传),“/usr/counts/out”为输出文件在HDFS中的位置。
4、实验操作:
步骤1 首先在Hadoop下使用 sbin/start-all.sh 命令启动集群。
步骤2 上传数据文件至HDFS
因为没有HDFS中没有 /usr/counters/in/ 目录,所以要先创建数据输入的路径。之后将放在/root/data/7/counters.txt的数据文件上传到刚刚创建好的路径
步骤3编写计数器程序。
步骤4打包程序。
步骤5 运行程序。