天天看點

好程式員大資料學習路線分享Hadoop機架感覺!/bin/bash

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

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

2.配置

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

2013-09-22 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配置檔案中配置一個選項:

topology.script.file.name

/home/bigdata/apps/hadoop/etc/hadoop/topology.sh

這個配置選項的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]}" = "$nodeArg" ]||[ "${ar[1]}" = "$nodeArg" ]; then

result="${ar[2]}"

fi

done

shift

if [ -z "$result" ] ; then

echo -n "/default-rack"

else

echo -n "$result"

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啟動時候日志是這樣的:

2013-09-23 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看到的結果:

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 看到的結果:

192.168.147.69:50010 (dbj69)

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

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

2013-11-21 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 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