hadoop之 hadoop 機架感知

張衝andy發表於2017-10-13

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 等 

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31383567/viewspace-2145945/,如需轉載,請註明出處,否則將追究法律責任。

相關文章