《Hadoop
权威指南》读书笔记之六 — Chapter 6
1. xml
文件的读取
1.1 读取步骤
- 01.添加在
resources
文件夹中 - 02.使用
Configuration
类的 addResource()
方法 - 03.获取
.xml
文件中的属性 - 04.
xml
文件可以通过 variable expansion
的方式进行设置。 但是这个设置值的顺序是不是得有个先后呢?这个定义值的顺序是没有先后关系要求的,只要属性是全局唯一的,那么就能获取到。 - 05.
xml
文件中如果某个属性是finla修饰,则不能够在其它的.xml
文件中重置了。
1.2 实战代码
import org.apache.hadoop.conf.Configuration;
public class PrintConfiguration {
public static void main(String[] args) {
Configuration conf = new Configuration();
conf.addResource("configuration-1.xml");
conf.addResource("configuration-2.xml");
System.out.println(conf.get("color"));
//getInt: Get the value of the name property as an int.
//如果不存在值,则直接返回 defaultValue
System.out.println(conf.getInt("size",0));
System.out.println("weight: "+conf.get("weight"));
//variable expansion
System.out.println("size-weight: "+conf.get("size-weight"));
//
System.setProperty("length", "2");
System.out.println("length: "+conf.get("length"));
}
}
执行结果如下:
-
configuration-1.xml
<?xml version="1.0"?>
<configuration>
<property>
<name>size-weight</name>
<value>${size},${weight}</value>
<description>Size and weight</description>
</property>
<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>
</configuration>
-
configuration-2.xml
<?xml version="1.0"?>
<configuration>
<property>
<name>color</name>
<value>blue</value>
<description>Color</description>
</property>
<property>
<name>size</name>
<value>20</value>
<description>Size</description>
</property>
<property>
<name>weight</name>
<value>light</value>
<description>Weight</description>
</property>
</configuration>
1.3 注意事项
- 这里
configuration-2.xml
文件中有一个属性weight
是对 configuration-1.xml
属性的覆写,但是因为configuration-1.xml
中的weight
属性是final
的,且代码的顺序是先加载configuration-1.xml
,再加载configuration-2.xml
文件,所以导致运行的时候会出现一个覆写警告。
Note that although configuration properties can be defined in terms of system properties, unless system properties are redefined using configuration properties, they are not accessible through the configuration API.
注意:尽管在系统属性方面可以定义配置属性,【这句话我不是很理解】但是除非系统属性被重定义在配置文件中,否则它们是不能通过配置的API访问的。【例如:上面的length
属性,会得到一个null值】。
2. Tool 类详解
在了解Tool
类之前,先了解一下GenericOptionsParser
类。
2.1 GenericOptionsParser
GenericOptionsParser is a class that interprets common Hadoop command-line options and sets them on a Configuration object for your application to use as desired.
但是我们并不需要这么麻烦,因为很多时候,我们可以直接使用Tool
类就可以解决问题。因为Tool
内部其实就是使用了GenericOptionParser
类。因为Tool
类 继承自Configurable
类
public interface Tool extends Configurable {
int run(String [] args) throws Exception;
}
2.2 Tool
所有的 Tool
的实现类同时需要实现 Configurable
类 (因为Tool 继承了该类)。并且其(Configurable
)子类Configured
是最简单的实现。
2.3 实战代码
package hadoopDefinitiveGuide.chapter_6;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;
import java.util.Map;
public class ConfigurationPrinter extends Configured implements Tool {
static
{
Configuration.addDefaultResource("hdfs-default.xml");
}
@Override
public int run(String[] args) throws Exception {
//这里可以直接调用getConf() 方法是因为:getConf()是从Configured 中继承来的
//而 Configured 的方法是从 Configurable 中实现而来的。
Configuration conf = getConf();
for (Map.Entry<String, String> entry : conf) {
System.out.printf("%s=%s\n", entry.getKey(), entry.getValue());
}
return 0;
}
public static void main(String[] args) throws Exception {
int exitCode = ToolRunner.run(new ConfigurationPrinter(), args);
System.exit(exitCode);
}
}
其中省略了部分输出。得到如上结果。
但是我们代码里又用到了ToolRunner
类,这个是干嘛的呢? 查看该方法如下:
可以看到这个 run()
其实底层调用了 另一个run()
方法, 但是在运行之前添加了一个tool.getConf()
参数,这个getConf()
方法是用于得到一个Configuration
实例,从而传递给run()
方法作为参数。接着调用的run()
方法如下:
可以看到里面分别维持了三个重要对象:conf
,parser
和tool
接着调用Tool
类的 run
方法,运行程序。
3. 详解如下两个类/接口
3.1 Configurable
是什么
3.2 Configured
是什么
3.3 二者有什么区别?
Configurable 接口
Configured 类
4.属性配置
并不是所有的属性都可以在 client
中配置。比如说 yarn.nodemanager.resource.memeory-mb
这个配置就必须在 yarn-site.xml
中配置,否则无效。
5.在Hadoop 2 之后,配置的命名规则
- 01.与 HDFS 属性相关的namenode 已经改为使用
dfs.namenode
作为前缀了。 - 02.与MapReduce 属性相关的 则是有一个 mapreduce前缀,而不再是mapred 前缀了。如:
mapreduce.job.name
6. Hadoop
和 JVM
设置属性的区别
- 01.注意使用
GenericOptionsParser
这个类去设置hadoop 的属性时,其语法是【-D property=value
】这个类和 JVM中的 -Dproperty=value
不同。注意-D 和 property的空格。 - 02.JVM 的系统属性值是从
java.lang.System
类中读取的; Hadoop 的属性值仅仅是从 Configuration
对象中获取
7. 为什么 Mapper
方法中的 <KeyIn,valuIn>
通常都是 Longwritable
, 和Text
?
- 因为大多数 的Mapper 任务都是需要从hdfs从读取文件的,而大多数的文件都是默认的.txt文件,即可以直接读取的。
- Mapper 的默认读取类型是
TextInputFormat
类,从而使得KeyIn = LongWritable, 而 valueIn = Text
。
8. MapReduce
的job在不同的平台上运行的效果
The local job runner uses a single JVM to run a job, so as long as all the classes that your
job needs are on its classpath, then things will just work.
本地作业运行程序使用单个jvm 来运行作业,因此只要作业所需的所有类都在其类路径中,那么就会起作用。
但是集群中的作业运行可能稍有不同。如果需要将写好的代码放到集群中运行,那么分为如下几步:
- step 1:packaged into a job JAR file to send to the cluster
- step 2:Hadoop will find the job JAR automatically by searching for the JAR on the driver’s classpath that contains the class set in the setJarByClass() method (on JobConf or Job).
- step 3:Any dependent JAR files can be packaged in a lib subdirectory in the job JAR file, although there are other ways to include dependencies, discussed later.
- step 4:Similarly, resource files can be packaged in a classes subdirectory.
在一个集群(包括伪分布集群)上,map 以及 reduce 任务都是运行在单独节点的jvm上的。并且它们的类路径并不是由HADOOP_CLASSPATH
控制的。
HADOOP_CLASSPATH
是一个客户端设置并且仅仅为提交作业的驱动的JVM
设置。相反,用户的作业路径包含如下项:
- The job JAR file
- Any JAR files contained in the lib directory of the job JAR file, and the classes directory (if present)
- Any files added to the distributed cache using the -libjars option , or the addFileToClassPath() method on DistributedCache (old API), or Job (new API)
10.User JAR
文件可以被添加到client classpath,以及task classpath之后,在这种情况下,这些用户的jar包可能会导致和 hadoop自带的jar包产生冲突。
解决这种问题的办法常用:
- 调整任务的 classpath 顺序,从而让你的类能够被首次获取
- 在客户端,可以设置 HADOOP_USER_CLASSPATH_FIRST 环境变量为true
11. MapReduce job IDs
的生成规则
11.1 生成规则
-
application
前缀
这个前缀是固定的,代表的是一个MapReduce应用 -
Yarn application IDs
这个ids 由 YARN resources manager 生成,是一个时间戳。 - 一个自增的
id
这个自增的id 也是由 YARN resource Manager
生成,代表的是这个resource manager
所处理的应用数
11.2 针对id = application_1410450250506_0003
举例
- 这是一个
application
- 该任务是在
timestamp = 1410450250506
这个时候创建的; - 某个yarn 的第三个任务;
前导零的存在是为了 更好的排序,更好的展示出来。如果这个自增的id变成了五位数,这个数字会继续保留下去,而不是重置为0。
同理,job的id,也是这么生成的,只不过是将前缀 application
替换成了job
。 例如:task_1410450250506_0003_m_000003
则表示是 job_1410450250506_0003
的第四个【000003
,从0计数】map
任务。
attempt_1410450250506_0003_m_000003_0
则表示的是 task
重试的次数。
因为task可能被执行超过一次,所以需要添加一个数字标记。 比如说上面的这个标志, 其后面的0 表示的则是第一次尝试运行【是从0开始计数】
12. Job history
Job history refers to the events and configuration for a completed MapReduce job. It is retained regardless of whether the job was successful.
Job history files are stored in HDFS by the MapReduce application master, in a directory set by the
mapreduce.jobhistory.done-dir property.
Job history files are kept for one week before being deleted by the system.
The history log includes job, task, and attempt events, all of which are stored in a file in JSON format
13. job 调优
13.1 调优前言
在开发job之后,开发者经常想到的问题就是:“可以让这个job运行的更快一点儿嘛?” 在Hadoop中有一些通常的可疑项,这些可疑项是值的检验并查看是否引起了性能问题。在你剖析以及优化任务之前,你应该检查如下这份清单。
13.2 Profiling Tasks
Hadoop allows you to profile a fraction of the tasks in a job and, as each task completes, pulls down the profile information to your machine for later analysis with standard profiling tools.
上面这句话的意思说的就是:Hadoop 允许开发者对一个job中的某个任务进行侧面剖查,并且在每个任务完成时,获取剖查信息,用于后期使用标准的profiling tools
的分析
在分布式的集群中剖析应用充满许多挑战。
- 针对不同的瓶颈进行优化【如果一个任务是I/O繁忙型,就不应该去优化cpu】
- 不应该仅仅比对程序运行时间【因为集群在不同的时候的资源是不同的】
13.3 开启 profile
功能
Enabling profiling is as simple as setting the property mapreduce.task.profile to true
例如:在运行任务的时候,指定参数 -D mapreduce.task.profile=true
。
如:hadoop jar hadoop-examples.jar -D mapreduce.task.profile=true ....
13.4 注意事项
- 避免分析所有的task
要分析所有的运行的task,通常是没有意义的,因为通过数个task
就能够看到程序哪里出了问题。所以默认分析的程序只有 id =0,1,2 会被分析。可以通过设置 mapreduce.task.profile.maps
以及 mapreduce.task.profile.reduces
参数去指定 profile
的task id
区间。