实验七 MapReduce实验:计数器

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Will_cruise/article/details/88749455

实验指导:

7.1 实验目的

基于MapReduce思想,编写计数器程序。

7.2 实验要求

能够理解MapReduce编程思想,然后会编写MapReduce版本计数器程序,并能执行该程序和分析执行过程。

7.3 实验背景

7.3.1 MapReduce计数器是什么?

计数器是用来记录job的执行进度和状态的。它的作用可以理解为日志。我们可以在程序的某个位置插入计数器,记录数据或者进度的变化情况。

7.3.2 MapReduce计数器能做什么?

MapReduce 计数器(Counter)为我们提供一个窗口,用于观察 MapReduce Job 运行期的各种细节数据。对MapReduce性能调优很有帮助,MapReduce性能优化的评估大部分都是基于这些 Counter 的数值表现出来的。

在许多情况下,一个用户需要了解待分析的数据,尽管这并非所要执行的分析任务 的核心内容。以统计数据集中无效记录数目的任务为例,如果发现无效记录的比例 相当高,那么就需要认真思考为何存在如此多无效记录。是所采用的检测程序存在 缺陷,还是数据集质量确实很低,包含大量无效记录?如果确定是数据集的质量问 题,则可能需要扩大数据集的规模,以增大有效记录的比例,从而进行有意义的分析。

计数器是一种收集作业统计信息的有效手段,用于质量控制或应用级统计。计数器 还可辅助诊断系统故障。如果需要将日志信息传输到map或reduce任务,更好的 方法通常是尝试传输计数器值以监测某一特定事件是否发生。对于大型分布式作业 而言,使用计数器更为方便。首先,获取计数器值比输出日志更方便,其次,根据 计数器值统计特定事件的发生次数要比分析一堆日志文件容易得多。 

7.3.3 内置计数器

MapReduce 自带了许多默认Counter,现在我们来分析这些默认 Counter 的含义,方便大家观察 Job 结果,如输入的字节数、输出的字节数、Map端输入/输出的字节数和条数、Reduce端的输入/输出的字节数和条数等。下面我们只需了解这些内置计数器,知道计数器组名称(groupName)和计数器名称(counterName),以后使用计数器会查找groupName和counterName即可。

7.3.4 计数器使用

7.3.4.1 定义计数器

枚举声明计数器:

// 自定义枚举变量Enum
Counter counter = context.getCounter(Enum enum)

自定义计数器:

// 自己命名groupName和counterName
Counter counter = context.getCounter(String groupName,String counterName)

7.3.4.2 为计数器赋值

初始化计数器:

counter.setValue(long value);//设置初始值

计数器自增:

counter.increment(long incr);// 增加计数

7.3.4.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);// 查找枚举计数器,假如Enum的变量为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();
// 查找作业运行启动的reduce个数的计数器,groupName和counterName可以从内置计数器表格查询(前面已经列举有)
Counter counter=counters.findCounter("org.apache.hadoop.mapreduce.JobCounter","TOTAL_LAUNCHED_REDUCES") ;// 假如groupName为org.apache.hadoop.mapreduce.JobCounter,counterName为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());
  }
}

7.3.5 自定义计数器

MapReduce允许用户编写程序来定义计数器,计数器的值可在mapper或reducer 中增加。多个计数器由一个Java枚举(enum)类型来定义,以便对计数器分组。一个作业可以定义的枚举类型数量不限,各个枚举类型所包含的字段数量也不限。枚 举类型的名称即为组的名称,枚举类型的字段就是计数器名称。计数器是全局的。换言之,MapReduce框架将跨所有map和reduce聚集这些计数器,并在作业结束 时产生一个最终结果。

7.4 实验步骤

7.4.1 实验分析设计

该实验要求学生自己实现一个计数器,统计输入的无效数据。说明如下:

假如一个文件,规范的格式是3个字段,“\t”作为分隔符,其中有2条异常数据,一条数据是只有2个字段,一条数据是有4个字段。其内容如下所示:

jim     1     28
kate    0     26
tom    1
lily     0     29    22

编写代码统计文档中字段不为3个的异常数据个数。如果字段超过3个视为过长字段,字段少于3个视为过短字段。

7.4.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);
	}
}

7.4.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中的位置。

7.5 实验结果

7.5.1 输入数据

输入数据如下:counters.txt(\t分割)(数据统一放在/root/data目录下)。

jim     1     28
kate    0     26
tom    1
lily     0     29    22

7.5.2 输出显示

如图7-1所示:

图7-1

实验操作:

步骤1 搭建Hadoop集群

首先在Hadoop下使用 sbin/start-all.sh 命令启动集群

步骤2 上传数据文件至HDFS

因为没有HDFS中没有 /usr/counters/in/ 目录,所以要先创建数据输入的路径

之后将放在/root/data/7/counters.txt的数据文件上传到刚刚创建好的路径

步骤3

编写计数器程序

步骤4

打包程序

步骤5 运行程序

此处应注意打包时的jar包名称、主类名称以及输入输出路径,尤其注意大小写,很容易出错,博主多次入坑,需加倍留心。

步骤6 查看运行结果

猜你喜欢

转载自blog.csdn.net/Will_cruise/article/details/88749455