天天看點

hadoop 配置機架感覺

轉載請注明出處:http://blog.csdn.net/l1028386804/article/details/51935169

1.背景

      Hadoop在設計時考慮到資料的安全與高效,資料檔案預設在HDFS上存放三份,存儲政策為本地一份,同機架内其它某一節點上一份,不同機架的某一節點上一份。這樣如果本地資料損壞,節點可以從同一機架内的相鄰節點拿到資料,速度肯定比從跨機架節點上拿資料要快;同時,如果整個機架的網絡出現異常,也能保證在其它機架的節點上找到資料。為了降低整體的帶寬消耗和讀取延時,HDFS會盡量讓讀取程式讀取離它最近的副本。如果在讀取程式的同一個機架上有一個副本,那麼就讀取該副本。如果一個HDFS叢集跨越多個資料中心,那麼用戶端也将首先讀本地資料中心的副本。那麼Hadoop是如何确定任意兩個節點是位于同一機架,還是跨機架的呢?答案就是機架感覺。

     預設情況下,hadoop的機架感覺是沒有被啟用的。是以,在通常情況下,hadoop叢集的HDFS在選機器的時候,是随機選擇的,也就是說,很有可能在寫資料時,hadoop将第一塊資料block1寫到了rack1上,然後随機的選擇下将block2寫入到了rack2下,此時兩個rack之間産生了資料傳輸的流量,再接下來,在随機的情況下,又将block3重新又寫回了rack1,此時,兩個rack之間又産生了一次資料流量。在job處理的資料量非常的大,或者往hadoop推送的資料量非常大的時候,這種情況會造成rack之間的網絡流量成倍的上升,成為性能的瓶頸,進而影響作業的性能以至于整個叢集的服務

2.配置

  預設情況下,namenode啟動時候日志是這樣的:

2016-07-17 17:27:26,423 INFO org.apache.hadoop.net.NetworkTopology: Adding a new node:  /default-rack/ 192.168.147.92:50010
           

每個IP 對應的機架ID都是 /default-rack ,說明hadoop的機架感覺沒有被啟用。

要将hadoop機架感覺的功能啟用,配置非常簡單,在 NameNode所在節點的/home/bigdata/apps/hadoop/etc/hadoop的core-site.xml配置檔案中配置一個選項:

<property>
  <name>topology.script.file.name</name>
  <value>/home/bigdata/apps/hadoop/etc/hadoop/topology.sh</value>
</property>
           

這個配置選項的value指定為一個可執行程式,通常為一個腳本,該腳本接受一個參數,輸出一個值。接受的參數通常為某台datanode機器的ip位址,而輸出的值通常為該ip位址對應的datanode所在的rack,例如”/rack1”。Namenode啟動時,會判斷該配置選項是否為空,如果非空,則表示已經啟用機架感覺的配置,此時namenode會根據配置尋找該腳本,并在接收到每一個datanode的heartbeat時,将該datanode的ip位址作為參數傳給該腳本運作,并将得到的輸出作為該datanode所屬的機架ID,儲存到記憶體的一個map中.

      至于腳本的編寫,就需要将真實的網絡拓樸和機架資訊了解清楚後,通過該腳本能夠将機器的ip位址和機器名正确的映射到相應的機架上去。一個簡單的實作如下:

#!/bin/bash
HADOOP_CONF=/home/bigdata/apps/hadoop/etc/hadoop
while [ $# -gt 0 ] ; do
  nodeArg=$1
  exec<${HADOOP_CONF}/topology.data
  result=""
  while read line ; do
    ar=( $line )
    if [ "${ar[0]}&quot; = &quot;$nodeArg" ]||[ "${ar[1]}&quot; = &quot;$nodeArg" ]; then
      result="${ar[2]}"
    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和主機名都配上。

這樣配置後,namenode啟動時候日志是這樣的:

2016-07-17 17:16:27,272 INFO org.apache.hadoop.net.NetworkTopology: Adding a new node:  /dc1/rack3/  192.168.147.94:50010
           

說明hadoop的機架感覺已經被啟用了。

檢視HADOOP機架資訊指令:

./hadoop dfsadmin -printTopology 
Rack: /dc1/rack1
   192.168.147.91:50010 (tbe192168147091)
   192.168.147.92:50010 (tbe192168147092)

Rack: /dc1/rack2
   192.168.147.93:50010 (tbe192168147093)

Rack: /dc1/rack3
   192.168.147.94:50010 (tbe192168147094)
   192.168.147.95:50010 (tbe192168147095)
   192.168.147.96:50010 (tbe192168147096)
           

3.增加資料節點,不重新開機NameNode

假設Hadoop叢集在192.168.147.68上部署了NameNode和DataNode,啟用了機架感覺,執行bin/hadoop dfsadmin -printTopology看到的結果:

Rack: /dc1/rack1

   192.168.147.68:50010 (dbj68)

現在想增加一個實體位置在rack2的資料節點192.168.147.69到叢集中,不重新開機NameNode。

首先,修改NameNode節點的topology.data的配置,加入:192.168.147.69 dbj69 /dc1/rack2,儲存。

192.168.147.68 dbj68 /dc1/rack1
192.168.147.69 dbj69 /dc1/rack2
           

然後,sbin/hadoop-daemons.sh start datanode啟動資料節點dbj69,任意節點執行bin/hadoop dfsadmin -printTopology 看到的結果:

Rack: /dc1/rack1
   192.168.147.68:50010 (dbj68)

Rack: /dc1/rack2
   192.168.147.69:50010 (dbj69)
           

說明hadoop已經感覺到了新加入的節點dbj69。

注意:如果不将dbj69的配置加入到topology.data中,執行sbin/hadoop-daemons.sh start datanode啟動資料節點dbj69,datanode日志中會有異常發生,導緻dbj69啟動不成功。

2016-07-17 10:51:33,502 FATAL org.apache.hadoop.hdfs.server.datanode.DataNode: Initialization failed for block pool Block pool BP-1732631201-192.168.147.68-1385000665316 (storage id DS-878525145-192.168.147.69-50010-1385002292231) service to dbj68/192.168.147.68:9000
org.apache.hadoop.ipc.RemoteException(org.apache.hadoop.net.NetworkTopology$InvalidTopologyException): Invalid network topology. You cannot have a rack and a non-rack node at the same level of the network topology.
  at org.apache.hadoop.net.NetworkTopology.add(NetworkTopology.java:382)
  at org.apache.hadoop.hdfs.server.blockmanagement.DatanodeManager.registerDatanode(DatanodeManager.java:746)
  at org.apache.hadoop.hdfs.server.namenode.FSNamesystem.registerDatanode(FSNamesystem.java:3498)
  at org.apache.hadoop.hdfs.server.namenode.NameNodeRpcServer.registerDatanode(NameNodeRpcServer.java:876)
  at org.apache.hadoop.hdfs.protocolPB.DatanodeProtocolServerSideTranslatorPB.registerDatanode(DatanodeProtocolServerSideTranslatorPB.java:91)
  at org.apache.hadoop.hdfs.protocol.proto.DatanodeProtocolProtos$DatanodeProtocolService$2.callBlockingMethod(DatanodeProtocolProtos.java:20018)
  at org.apache.hadoop.ipc.ProtobufRpcEngine$Server$ProtoBufRpcInvoker.call(ProtobufRpcEngine.java:453)
  at org.apache.hadoop.ipc.RPC$Server.call(RPC.java:1002)
  at org.apache.hadoop.ipc.Server$Handler$1.run(Server.java:1701)
  at org.apache.hadoop.ipc.Server$Handler$1.run(Server.java:1697)
  at java.security.AccessController.doPrivileged(Native Method)
  at javax.security.auth.Subject.doAs(Subject.java:415)
  at org.apache.hadoop.security.UserGroupInformation.doAs(UserGroupInformation.java:1408)
  at org.apache.hadoop.ipc.Server$Handler.run(Server.java:1695)

  at org.apache.hadoop.ipc.Client.call(Client.java:1231)
  at org.apache.hadoop.ipc.ProtobufRpcEngine$Invoker.invoke(ProtobufRpcEngine.java:202)
  at $Proxy10.registerDatanode(Unknown Source)
  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  at java.lang.reflect.Method.invoke(Method.java:601)
  at org.apache.hadoop.io.retry.RetryInvocationHandler.invokeMethod(RetryInvocationHandler.java:164)
  at org.apache.hadoop.io.retry.RetryInvocationHandler.invoke(RetryInvocationHandler.java:83)
  at $Proxy10.registerDatanode(Unknown Source)
  at org.apache.hadoop.hdfs.protocolPB.DatanodeProtocolClientSideTranslatorPB.registerDatanode(DatanodeProtocolClientSideTranslatorPB.java:149)
  at org.apache.hadoop.hdfs.server.datanode.BPServiceActor.register(BPServiceActor.java:619)
  at org.apache.hadoop.hdfs.server.datanode.BPServiceActor.connectToNNAndHandshake(BPServiceActor.java:221)
  at org.apache.hadoop.hdfs.server.datanode.BPServiceActor.run(BPServiceActor.java:660)
  at java.lang.Thread.run(Thread.java:722)
           

4.節點間距離計算

有了機架感覺,NameNode就可以畫出下圖所示的datanode網絡拓撲圖。D1,R1都是交換機,最底層是datanode。則H1的rackid=/D1/R1/H1,H1的parent是R1,R1的是D1。這些rackid資訊可以通過topology.script.file.name配置。有了這些rackid資訊就可以計算出任意兩台datanode之間的距離,得到最優的存放政策,優化整個叢集的網絡帶寬均衡以及資料最優配置設定。

distance(/D1/R1/H1,/D1/R1/H1)=0  相同的datanode
distance(/D1/R1/H1,/D1/R1/H2)=2  同一rack下的不同datanode
distance(/D1/R1/H1,/D1/R2/H4)=4  同一IDC下的不同datanode
distance(/D1/R1/H1,/D2/R3/H7)=6  不同IDC下的datanode