hadoop之 hadoop 机架感知

Posted 张冲andy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了hadoop之 hadoop 机架感知相关的知识,希望对你有一定的参考价值。

1.背景

Hadoop在设计时考虑到数据的安全与高效,数据文件默认在HDFS上存放三份,存储策略为本地一份,同机架内其它某一节点上一份,不同机架的某一节点上一份。这样如果本地数据损坏,节点可以从同一机架内的相邻节点拿到数据,速度肯定比从跨机架节点上拿数据要快;同时,如果整个机架的网络出现异常,也能保证在其它机架的节点上找到数据。为了降低整体的带宽消耗和读取延时,HDFS会尽量让读取程序读取离它最近的副本。如果在读取程序的同一个机架上有一个副本,那么就读取该副本。如果一个HDFS集群跨越多个数据中心,那么客户端也将首先读本地数据中心的副本。那么Hadoop是如何确定任意两个节点是位于同一机架,还是跨机架的呢?答案就是机架感知。

默认情况下,hadoop的机架感知是没有被启用的。所以,在通常情况下,hadoop集群的HDFS在选机器的时候,是随机选择的,也就是说,很有可能在写数据时,hadoop将第一块数据block1写到了rack1上,然后随机的选择下将block2写入到了rack2下,此时两个rack之间产生了数据传输的流量,再接下来,在随机的情况下,又将block3重新又写回了rack1,此时,两个rack之间又产生了一次数据流量。在job处理的数据量非常的大,或者往hadoop推送的数据量非常大的时候,这种情况会造成rack之间的网络流量成倍的上升,成为性能的瓶颈,进而影响作业的性能以至于整个集群的服务

2.配置

两种方式来配置机架感知。一种是通过配置一个脚本来进行映射;另一种是通过实现DNSToSwitchMapping接口的resolve()方法来完成网络位置的映射。

  hadoop自身是没有机架感知能力的,必须通过人为的设定来达到这个目的。在FSNamesystem类中的resolveNetworkLocation()方法负载进行网络位置的转换。其中dnsToSwitchMapping变量代表了完成具体转换工作的类,其值如下:

this.dnsToSwitchMapping = ReflectionUtils.newInstance(
conf.getClass("topology.node.switch.mapping.impl", ScriptBasedMapping.class,
DNSToSwitchMapping.class), conf);

也就是说dnsToSwitchMapping的值由“core-site.xml”配置文件中的"topology.node.switch.mapping.impl"参数指定。默认值为ScriptBasedMapping,也就是通过读提前写好的脚本文件来进行网络位置映射的。但如果这个脚本没有配置的话,那就使用默认值“default-rack”作为所有结点的网络位置。

下面就先说说第一种配置机架感知的方法,使用脚本来完成网络位置的映射。

要将hadoop机架感知的功能启用,配置非常简单,在NameNode所在节点的/home/bigdata/apps/hadoop-talkyun/etc/hadoop的core-site.xml配置文件中配置一个选项:

<property>
<name>topology.script.file.name</name>
<value>/home/bigdata/apps/hadoop-talkyun/etc/hadoop/topology.sh</value>
</property>
这个配置选项的value指定为一个可执行程序,通常为一个脚本,该脚本接受一个参数,输出一个值。接受的参数通常为某台datanode机器的ip地址,而输出的值通常为该ip地址对应的datanode所在的rack,例如”/rack1”。Namenode启动时,会判断该配置选项是否为空,如果非空,则表示已经启用机架感知的配置,此时namenode会根据配置寻找该脚本,并在接收到每一个datanode的heartbeat时,将该datanode的ip地址作为参数传给该脚本运行,并将得到的输出作为该datanode所属的机架ID,保存到内存的一个map中.

至于脚本的编写,就需要将真实的网络拓朴和机架信息了解清楚后,通过该脚本能够将机器的ip地址和机器名正确的映射到相应的机架上去。一个简单的实现如下:


在wiki上找到一个官方的配置脚本,可以参考一下。首先是shell脚本:
topology.sh:

#!/bin/bash
HADOOP_CONF=/etc/hadoop/conf
while [ $# -gt 0 ] ; do //$#代表执行命令时输入的参数个数
nodeArg=$1
exec< ${HADOOP_CONF}/topology.data //读入文件
result=""
while read line ; do //循环遍历文件内容
ar=( $line )
if [ "${ar[0]}" = "$nodeArg" ] ; then
result="${ar[1]}"
fi
done
shift
if [ -z "$result" ] ; then
echo -n "/default/rack "
else
echo -n "$result "
fi
done

topology.data,格式为:节点(ip或主机名) /交换机xx/机架xx

192.168.147.91 tbe192168147091 /dc1/rack1
192.168.147.92 tbe192168147092 /dc1/rack1
192.168.147.93 tbe192168147093 /dc1/rack2
192.168.147.94 tbe192168147094 /dc1/rack3
192.168.147.95 tbe192168147095 /dc1/rack3
192.168.147.96 tbe192168147096 /dc1/rack3
需要注意的是,在Namenode上,该文件中的节点必须使用IP,使用主机名无效,而Jobtracker上,该文件中的节点必须使用主机名,使用IP无效,所以,最好ip和主机名都配上。

第二种配置机架感知的方法是通过实现DNSToSwitchMapping接口,重写resolve()方法完成的。这就需要自己写个java类来完成映射了。然后在“core-site.xml”配置文件中的“topology.node.switch.mapping.impl”指定自己的实现类。这样的话,在进行网络位置解析的时候,就会调用自己类中的resolve()方法来完成转换了。我写的比较简单,能完成功能就好,代码如下(大神飞过):

public class MyResolveNetworkTopology implements DNSToSwitchMapping {

private String[] hostnameLists = {"tt156", "tt163", "tt164", "tt165"};
private String[] ipLists = {"10.32.11.156", "10.32.11.163", "10.32.11.164", "10.32.11.165"};
private String[] resolvedLists = {"/dc1/rack1", "/dc1/rack1", "/dc1/rack2", "/dc1/rack2"};

@Override
public List<String> resolve(List<String> names) {
names = NetUtils.normalizeHostNames(names);

List <String> result = new ArrayList<String>(names.size());
if (names.isEmpty()) {
return result;
}

for (int i = 0; i < names.size(); i++) {
String name = names.get(i);
for(int j = 0; j < hostnameLists.length; j++){
if(name.equals(hostnameLists[j])) {
result.add(resolvedLists[j]);
} else if(name.equals(ipLists[j])) {
result.add(resolvedLists[j]);
}
}
}
return result;
}

我把这个自定义的MyResolveNetworkTopology类放在了core包的org.apache.hadoop.net目录下。所以在“core-site.xml”文件中的配置如下:  

<property>
<name>topology.node.switch.mapping.impl</name>
<value>org.apache.hadoop.net.MyResolveNetworkTopology</value>
<description> The default implementation of the DNSToSwitchMapping. It
invokes a script specified in topology.script.file.name to resolve
node names. If the value for topology.script.file.name is not set, the
default value of DEFAULT_RACK is returned for all node names.
</description>
</property>

以上两种方法在配置完成后,会在NameNode和JobTracker的log中打印出如下信息:
INFO org.apache.hadoop.net.NetworkTopology: Adding a new node: /dc1/rack3/ 192.168.147.94:50010

 这就说明机架感知配置成功了。

  总结一下以上两种方式。通过脚本配置的方式,灵活性很高,但是执行效率较低。因为系统要从jvm转到shell去执行;java类的方式性能较高,但是编译之后就无法改变了,所以灵活程度较低。所以要根据具体情况来选择策略.


补充:
查看HADOOP机架信息命令:
./hadoop dfsadmin -printTopology

说明:整理于网络

source: http://www.cnblogs.com/gwgyk/p/4531947.html 等 

以上是关于hadoop之 hadoop 机架感知的主要内容,如果未能解决你的问题,请参考以下文章

hadoop:hdfs 机架感知

Hadoop机架感知原理

一文理解 Apache Hadoop 机架感知

[Hadoop]HDFS机架感知策略

HDFS机架感知

Apache Hadoop集群安装(NameNode HA + SPARK + 机架感知)