MapReduce编程实战之“调试”和"调优"
本篇内容
在上一篇的“初识”环节,我们已经在本地和Hadoop集群中,成功的执行了几个MapReduce程序,对MapReduce编程,已经有了最初的理解。
在本篇文章中,我们对MapReduce编程进行进一步的了解,包含:配置API、辅助类、调试手段、调优手段。
整体来说,我个人的理解是:
(1)本地开发阶段,对于Eclipse开发MapReduce程序来说。是不须要不论什么插件的,和开发普通的Java程序是一样的,通过DEBUG和单元測试排错。
(2)Hadoop环境測试阶段。也比較困难或者说比較麻烦进行远程调试。常常做的是打印语句。看日志。
配置API和辅助类
配置API
一个Configuration类的实例。代表配置属性及其取值的一个集合。maven项目的src/main/resources下有配置文件conf.xml,内容例如以下:
<?xml version="1.0"? >
<configuration>
<property>
<name>color</name>
<value>yellow</value>
<description>Color</description>
</property>
<property>
<name>size</name>
<value>10</value>
<description>Size</description>
</property>
<property>
<name>weight</name>
<value>heavy</value>
<final>true</final>
<description>Weight</description>
</property>
<property>
<name>size-weight</name>
<value>${size},${weight}</value>
<description>Sizeandwelght</description>
</property>
</configuration>
例如以下代码,能够读取配置文件的内容:
import org.apache.hadoop.conf.Configuration; public class ConfTest {
public static void main(String[] args) {
Configuration conf = new Configuration();
conf.addResource("conf.xml");
//注意:系统属性的优先级高于源文件里设置的属性。前提是size在conf.xml中有设置。否则就是null了
//对于这样写的。也能够用JVM參数 -Dproperty=value进行又一次设置
System.setProperty("size", "15"); System.out.println(conf.getInt("size", 0)); // 输出10
System.out.println(conf.get("weight"));
System.out.println(conf.get("size-weight")); //输出15,heavy
}
}
辅助类GenericOptionsParser/Tool/ToolRunner
GenericOptionsParser是一个类,用来解释经常使用的Hadoop命令行选项,依据须要,为Configuration对象设置对应的取值。通常不直接使用它,而是使用继承自它的接口Tool:实现Tool接口,通过ToolRunner来执行程序,ToolRunner内部调研GenericOptionsParser,例如以下:
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner; public class ConfTest extends Configured implements Tool { public static void main(String[] args) throws Exception {
int exitCode = ToolRunner.run(new ConfTest(), args);
System.exit(exitCode);
} public int run(String[] arg0) throws Exception {
Configuration conf = getConf();
conf.addResource("conf.xml");
System.out.println(conf.getInt("size", 0));
System.out.println(conf.get("weight"));
return 0;
}
}
在Hadoop集群中,我们运行命令:hadoop jar test.jar ConfTest -Dsize=188。会看到屏幕输出的是188。
我们也能够指定一个配置文件:hadoop jar test.jar -conf conf/xx.xml 。
本地測试
在开发阶段保证MapReduce逻辑正确。比較经常使用的是写单元測试代码。
或者直接在Eclipse里面设置断点,进行DEBUG,会更加高效和直观。
假设MapReduce在Windows环境的Eclipse中不能执行的话,请參照这里:
http://blog.csdn.net/puma_dong/article/details/23711103#t3
在集群上測试
hadoop集群是分布式的,可能有成百上千的机器,在机器中进行作业调试是非常困难的。一般来说,比較经典的办法是通过打印语句来调试程序。
我们把错误信息记录到标准错误中,同一时候更新任务状况(用reporter.setStatus()方法,可是这个功能在Hadoop2.2中貌似无效了),然后,在Web UI中,能够比較方便的看到这个错误日志。
用例程序代码
import java.io.IOException;
import java.util.Iterator;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapred.FileInputFormat;
import org.apache.hadoop.mapred.FileOutputFormat;
import org.apache.hadoop.mapred.JobClient;
import org.apache.hadoop.mapred.JobConf;
import org.apache.hadoop.mapred.MapReduceBase;
import org.apache.hadoop.mapred.Mapper;
import org.apache.hadoop.mapred.OutputCollector;
import org.apache.hadoop.mapred.Reducer;
import org.apache.hadoop.mapred.Reporter; public class MaxTemperatureReporter { public static void main(String[] args) throws Exception {
JobConf conf = new JobConf(MaxTemperatureReporter.class);
conf.setJobName("Max Temperature"); // FileInputFormat.addInputPaths(conf, new Path(args[0]));
// FileOutputFormat.setOutputPath(conf, new Path(args[1])); FileInputFormat.setInputPaths(conf, new Path("/test/input/t"));
FileOutputFormat.setOutputPath(conf, new Path("/test/output/t")); conf.setMapperClass(MaxTemperatureMapperReporter.class);
conf.setReducerClass(MaxTemperatureReduceReporter.class); conf.setOutputKeyClass(Text.class);
conf.setOutputValueClass(IntWritable.class); JobClient.runJob(conf);
}
} class MaxTemperatureMapperReporter extends MapReduceBase implements
Mapper<LongWritable, Text, Text, IntWritable> {
private static final int MISSING = 9999; enum Temperature {
OVER_10
} public void map(LongWritable key, Text value,
OutputCollector<Text, IntWritable> output, Reporter reporter)
throws IOException {
String line = value.toString();
String year = line.substring(15, 19);
int airTemperature;
if (line.charAt(87) == '+') {
airTemperature = Integer.parseInt(line.substring(88, 92));
} else {
airTemperature = Integer.parseInt(line.substring(87, 92));
}
try {
Thread.sleep(100); // 让程序执行的慢一点,能够看到执行过程
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (airTemperature > 100) {
System.err.println("Temperature over 10 degrees for input: "
+ value);
// 关于这个的作用,在Hadoop2.2的WebUI中貌似无效了
reporter.setStatus("Detected possibly corrupt record: see logs.");
// 这个就是符合某种情况的数据计数器。在WebUI能够看到统计
reporter.incrCounter(Temperature.OVER_10, 1);
}
String quality = line.substring(92, 93);
if (airTemperature != MISSING && quality.matches("[01459]")) {
output.collect(new Text(year), new IntWritable(airTemperature));
}
}
} class MaxTemperatureReduceReporter extends MapReduceBase implements
Reducer<Text, IntWritable, Text, IntWritable> {
public void reduce(Text key, Iterator<IntWritable> values,
OutputCollector<Text, IntWritable> output, Reporter reporter)
throws IOException {
int maxValue = Integer.MIN_VALUE;
while (values.hasNext()) {
maxValue = Math.max(maxValue, values.next().get());
}
output.collect(key, new IntWritable(maxValue)); }
}
在Hadoop集群执行
执行命令:hadoop jar test.jar MaxTemperatureReporter
启动时的截图:
MapReduce运行完成时的截图:
在MapReduce的运行过程中,也能够通过命令:
hadoop job -counter job_1397897643076_0010 'MaxTemperatureMapperReporter$Temperature' OVER_10
即时查看我们定义的计数器的情况,可是最直观的情况。还是通过WebUI查看。
WebUI即时查看Job执行状况
WebUI地址:http://master:8088/cluster 。
(1)WebUI首页截图:
(2)Job列表界面截图:
(3)Job信息界面截图:
(4)Task列表界面截图:
(5)Task计数器界面截图:
(6)Task Attempts界面截图:
(7)Task Logs首页截图:
(8)Task错误日志截图:
集群測试小结
我的环境是在2个PC上分别作了2个RHEL6.2虚拟机。6600行数据,在集群上执行了10次,每次Hadoop都是在同样的节点上启动两个Map任务。同一个节点上启动一个Reduce任务。有时在node1上(物理机A的虚拟机),有时在nod2上(物理机B的虚拟机)。
当Job执行完成后,点击history连接,会报错误连接,尚不知道原因。
远程调试
这样的调试手段是通过设置一些属性。找到要进行处理的节点的task attempt。启动IsolationRunner,等待Eclipse连接调试。
这样的手段稍后具体解说。
作业调优
作业调优检查表
大部分的MapReduce作业,都是I/O密集型的,优化代码的CPU性能是没有意义的,为了保证全部调整都是有效的,应该在实际集群上对照新老运行时间。
这实际也是非常困难的,由于作业运行时间会随着其它作业的资源争夺和调度器决定的任务顺序不同而发生改变。
为了在这类情况下得到较短的作业运行时间,必须不断运行(改变代码或不改变代码)。并检查是否有明显的改进。
另外另一种HPROF分析工具,也能够使用。
Map的数量
终于分片决定了Map的个数。
Map任务的个数也能通过使用JobConf 的 conf.setNumMapTasks(int num)方法来手动地设置。
这种方法可以用来添加map任务的个数,可是不能设定任务的个数小于Hadoop系统通过切割输入数据得到的值。
Reduce的数量
默认情况下。仅仅有一个reducer,因此。也就仅仅有一个分区。在这样的情况下,partitioner操作将因为全部数据都已放入同一个分区而无关紧要了。
如果有非常多reducer。了解HashPartitioner的作用就非常重要。如果键的散列函数足够好。那么记录将被均匀分到若干个reduce任务中,这样。具有同样键的记录将由同一个reduce任务进行处理。
单个reducer的默认配置对Hadoop新手而言非常easy上手。真实的应用中,作业都把他设置成一个较大的数字。否则因为全部的中间数据都会放到一个reducer任务中。从而导致作业效率极低。
注意,在本地作业执行骑上执行时。仅仅支持0个或1个reducer。
reducer最优个数与集群中可用的reducer任务槽数相关。总槽数有集群中节点数与每一个节点的任务槽数相乘得到。该值由mapred.tasktracker.reduce.tasks.maxinum属性的值决定。
一个经常使用的方法是:设置比总槽数略微少一些的reducer数,这回给reducer任务留有余地(容忍一些发生错误而不须要延长作业执行时间)。
假设reduce任务非常大。比較明智的做法是使用很多其它的reducer,使得任务粒度更小。这样一来,任务的失败才不至于显著影响作业运行时间。
每一个节点的任务槽数,就是指一个TaskTracker可以同一时候执行最多多少个map/reduce任务,默认是2。一般设为CPU个数的1-2倍。
内存/任务
在默认情况下,Hadoop为各个守护进程分配1000MB(1GB)内存。该值由hadoop-env.sh文件的HADOOP_HEAPSIZE參数控制。
此外,TaskTracker启动独立的子JVM以执行map和reduce任务。
因此,计算一个工作机器的最大内存需求时,须要综合考虑上述因素。
一个TaskTracker可以同一时候执行最多多少个map任务。由mapred.tasktracker.map.tasks.maximum属性控制,默认值是2个任务。
对应的,一个TaskTracker可以同一时候执行的最多reduce任务数由mapred.tasktracker.reduce.tasks.maximum属性控制。默认值也是2。
分配给每一个子JVM的内存量由mapred.child.java.opts属性决定,默认值是-Xmx200m。表示每一个任务分配200M内存。
综上所述。在默认情况下,一个工作机器会占用2800MB内存。
在一个TaskTracker上可以同一时候执行的任务数取决于一台机器有多少个处理器。
因为MapReduce作业一般是I/O-bound,因此将任务数设定为超出处理器数也有一定道理,可以获得更好的利用率。
至于究竟须要执行多少个任务。则历来与相关作业的CPU使用情况,但经验法则则是(包含map和reduce任务)与处理器数的比值最好是1和2之间。
比如。如果客户拥有8个处理器,并计划在各处理器上分别跑2个进程,则能够将mapred.tasktracker.map.tasks.maximum和mapred.tasktracker.reduce.tasks.maximum的值分别设置7(考虑到还有datanode和tasktracker这两个进程,这两项值不可设置为8)。总内存开销井高达7600MB。
对于配备8GB物理内存的机器。该Java内存分配方案是否合理还取决于同一时候执行在这台机器上的其它进程。假设这台机器还执行着Streaming和Pipes程序等,因为无法为这些进程分配足够内存。这个分配方案并不合理(并且分配到子节点的内存将会降低)。
此时,各进程在系统中不断切换,导致服务性能恶化。
精准的内存设置季度依赖于集群自身的特性。
可用使用一些工具监控集群的内存使用情况。以优化分配方案。比如Ganglia。
对于主节点来说。namenode、second namenode和jobtracker守护进程在默认情况下各使用1000MB内存,所以总计3000MB。
MapReduce编程实战之“调试”和"调优"的更多相关文章
- MapReduce编程实战之“高级特性”
本篇介绍MapReduce的一些高级特性,如计数器.数据集的排序和连接.计数器是一种收集作业统计信息的有效手段.排序是MapReduce的核心技术,MapReduce也可以运行大型数据集间的" ...
- NodeJS的代码调试和性能调优
本文转自我的个人博客. NodeJS 自 2009 年显露人间,到现在已经六个年头了,由于各种原因,中间派生出了个兄弟,叫做 iojs,最近兄弟继续合体,衍生出了 nodejs4.0 版本,这东西算是 ...
- 数据库MySQL调优实战经验总结<转>
数据库MySQL调优实战经验总结 MySQL 数据库的使用是非常的广泛,稳定性和安全性也非常好,经历了无数大小公司的验证.仅能够安装使用是远远不够的,MySQL 在使用中需要进行不断的调整参数或优化设 ...
- 数据库MySQL调优实战经验总结
MySQL 数据库的使用是非常的广泛,稳定性和安全性也非常好,经历了无数大小公司的验证.仅能够安装使用是远远不够的,MySQL 在使用中需要进行不断的调整参数或优化设置,才能够发挥 MySQL 的最大 ...
- Java性能调优实战,覆盖80%以上调优场景
Java 性能调优对于每一个奋战在开发一线的技术人来说,随着系统访问量的增加.代码的臃肿,各种性能问题便会层出不穷. 日渐复杂的系统,错综复杂的性能调优,都对Java工程师的技术广度和技术深度提出了更 ...
- mysql调优 基础
MySQL调优可以从几个方面来做: 1. 架构层:做从库,实现读写分离: 2.系统层次:增加内存:给磁盘做raid0或者raid5以增加磁盘的读写速度:可以重新挂载磁盘,并加上noatime参数,这样 ...
- Linux TCP并发请求溺出 调优
TCP并发请求溺出 调优:系统开启某个监听端口后,当多个TCP请求连接监听端后,会把多个请求交给backlog的默认监听队列由socket server一并处理,backlog有自己的队列长度默认12 ...
- CM记录-Hadoop参数调优
1.HDFS调优 a.设置合理的块大小(dfs.block.size) b.将中间结果目录设置为分布在多个磁盘以提升写入速度(mapred.local.dir) c.设置DataNode处理RPC的线 ...
- LAMP之Apache调优
一.环境的搭建 实验环境: 操作系统:Centos 7.4 [root@xuegod70 ~]# ls apr-1.6.3.tar.gz apr-util-1.6.1.tar.bz2 httpd-2. ...
随机推荐
- 一篇关于BEM命名规范
一直以来自己对命名都是比较混乱的,并没有一个比较好的格式来命名,最近自己碰巧学习到了BEM命名规范,我想谈谈自己的理解以供自己来学习,同时也可以和各位大佬一起学习. BEM是一个很有用的方法可以创建复 ...
- (转)iOS开发之Pch预编译文件的创建
本文转自 http://www.cnblogs.com/496668219long/p/4568265.html 在Xcode6之前,创建一个新工程xcode会在Supporting files文件夹 ...
- Mac下Python和Pycharm之virtualenv
一.python如何配置virtualenv 1.安装virtualenv pip3 install virtualenvpip install -i https://pypi.tuna.tsin ...
- java 罕见的依赖报错 jstat: error while loading shared libraries: libjli.so: cannot open shared object file: No such file or directory
java 都用了N长时间了,突然,意外地发现有一个依赖的so文件从来没找见过 # ldd /usr/bin/java linux-vdso.so.1 => (0x00007fffba76900 ...
- 【LeetCode】Palindrome Number(回文数)
这道题是LeetCode里的第9道题. 题目说的: 判断一个整数是否是回文数.回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数. 示例 1: 输入: 121 输出: true 示例 2: ...
- BNUOJ 6727 Bone Collector
Bone Collector Time Limit: 1000ms Memory Limit: 32768KB This problem will be judged on HDU. Origin ...
- numpy array_split()
numpy.array_split(ary, indices_or_sections, axis=0)[source] Split an array into multiple sub-arrays. ...
- nginx的简介和配置文件实例(一)
此文章配合 nginx配置文件解答 共同分享,了解. 一.nginx服务简介Nginx是一个高性能的HTTP和反向代理服务器 使用 Nginx 前必须了解的事项: 1)Nginx 本身只是一个 ...
- Python unittest 学习
import unittest class UTest(unittest.TestCase): def test_upper(self): self.assertEqual('foo'.upper() ...
- BZOJ 3566 [SHOI2014]概率充电器 ——期望DP
期望DP. 补集转化,考虑不能被点亮的情况, 然后就是三种情况,自己不能亮,父亲不能点亮它,儿子不能点亮它. 第一次计算比较容易,第二次计算的时候需要出去第一次的影响,因为一条线只能传导一次 #inc ...