【HDFS】hadoop的機架感知策略是啥?
瞭解hadoop的或多或少都聽說過機架感知策略,無論是balancer還是jobtracker分配作業、資料副本放置策略都會用到機架感知。那什麼叫機架感知?
首先故名思意機架感知就是感知機架,誰感知?就是hadoop系統嘛,更確切地說是hadoop能在系統內部建立一套伺服器和機架的位置拓撲圖,並且能識別系統節點的拓撲位置,知道了這些,才能做副本放置策略、作業本地化等更高層的設計。
難道說hadoop系統能自動感應叢集或者機房內部的網路拓撲結構?想想看,各個公司的機房拓撲或者網路結構都不一樣,採用的裝置型別也不相同,hadoop真的那麼吊能感受到?顯然不能!hadoop系統想獲得這個網路拓撲結構,需要系統管理員的幫助。
試想一下,hadoop能構建一幅網路拓撲圖,實際的網路拓撲圖又千變萬化,管理員該怎麼弄?所以這時候hadoop有必要設計一抽象的拓撲圖結構,管理員需要讓實際的網路拓撲結構儘量地與之適配。
Namenode的大管家FSNamesystem有兩個重要的成員:
NetworkTopology clusterMap = new NetworkTopology();
private DNSToSwitchMapping dnsToSwitchMapping;
這兩個東西就負責構建了機架及機架感知。
首先說拓撲邏輯類 NetworkTopology clusterMap = new NetworkTopology();這個NetworkTopology建構函式就搞了一把鎖就完事了:
public NetworkTopology() {
netlock = new ReentrantReadWriteLock();
}
從建構函式裡似乎看不出來啥東西。我看檢視這個clusterMap物件,會發現很多地方呼叫它的add、remove等操作,也許從這裡可以看清楚這個網路拓撲類的面貌:
首先傳遞進來的必須是個實現了Node物件,而Node實際上是一個介面類,通過Ctrl+t可以看出我們熟悉的datanodeInfo也是實現了這個介面,另外還有一個NetworkTopology的內部類InnerNode也實現了這個介面,啥是InnerNode先不管,接著往下看。public void add(Node node) { if (node==null) return; if( node instanceof InnerNode ) { throw new IllegalArgumentException( "Not allow to add an inner node: "+NodeBase.getPath(node)); } netlock.writeLock().lock(); try { Node rack = getNode(node.getNetworkLocation()); if (rack != null && !(rack instanceof InnerNode)) { throw new IllegalArgumentException("Unexpected data node " + node.toString() + " at an illegal network location"); } if (clusterMap.add(node)) { LOG.info("Adding a new node: "+NodeBase.getPath(node)); if (rack == null) { numOfRacks++; } } LOG.debug("NetworkTopology became:\n" + this.toString()); } finally { netlock.writeLock().unlock(); } }
上面的add方法先進行簡單地合法性判斷,然後拿到NetworkTopology構建方法裡的那把鎖,add方法在註冊datanode的時候呼叫,當時傳進來的物件是DatanodeDescriptor,注意這一句:
Node rack = getNode(node.getNetworkLocation());
getWorkLocation,怎麼得到網路位置?讓datanode描述符獲得網路位置,datanodeDescriptor繼承自DatanodeInfo物件,返回的就是一個string型別的location,初始化的時候直接寫死賦值/default-rack,如果中間沒有重新set,那麼返回的應該是/default-rack,但是datanodeInfo有個set方法,這是註冊的時候,想辦法給它確定的,這裡留下問題1,繼續往下:
上面得到一個rack的Node物件,下面又開始呼叫clusterMap的add方法,注意這個clusterMap是這樣的:
InnerNode clusterMap = new InnerNode(InnerNode.ROOT);
就是說這clusterMap是一個InnerNode物件,似乎代表了一個根,這時候繼續再看InnerNode,可以看到這個類
private class InnerNode extends NodeBase {
private ArrayList<Node> children=new ArrayList<Node>();
private int numOfLeaves;
其實是一棵樹,每個節點下面都可以掛一批孩子,由於還是是node節點,因此從node的介面實現發現,除了datanodedescripter之外就是innernode,所以說這是一棵樹。
現在有了根節點,嘗試將註冊進來的datanode安排進去,稱為add,在整個add過程中,都會頻繁出現getNetworkLocation方法,整個東西對理解整段程式碼阻礙太大了,不得不搞清楚問題1.
問題又回到namenode處理註冊的datanode這裡來:
private void resolveNetworkLocation (DatanodeDescriptor node) {
List<String> names = new ArrayList<String>(1);
if (dnsToSwitchMapping instanceof CachedDNSToSwitchMapping) {
// get the node's IP address
names.add(node.getHost());
} else {
// get the node's host name
String hostName = node.getHostName();
int colon = hostName.indexOf(":");
hostName = (colon==-1)?hostName:hostName.substring(0,colon);
names.add(hostName);
}
//特別需要注意這裡,解析一個節點屬於哪個機架,傳入的引數可以是機器的ip也可能是機器名,所以這裡要特別注意。在編寫解析指令碼的時候,必須要考慮到這兩種情況
//否則,可能有些機器解析不了。
// resolve its network location
List<String> rName = dnsToSwitchMapping.resolve(names);
String networkLocation;
if (rName == null) {
LOG.error("The resolve call returned null! Using " +
NetworkTopology.DEFAULT_RACK + " for host " + names);
networkLocation = NetworkTopology.DEFAULT_RACK;
} else {
networkLocation = rName.get(0);
}
node.setNetworkLocation(networkLocation);
}
整個方法用到dnsToSwitchMapping物件的resolve方法,關鍵的東西來了,這個DNSToSwitchMapping介面就完成了拓撲結構的對映,目前只實現了CachedDNSToSwitchMapping這種,從上面的方法可以看出來你把機器IP輸入,通過resolve方法直接告訴了你網路位置。ok,進去看看吧, public List<String> resolve(List<String> names) {
// normalize all input names to be in the form of IP addresses
names = NetUtils.normalizeHostNames(names);
List <String> result = new ArrayList<String>(names.size());
if (names.isEmpty()) {
return result;
}
List<String> uncachedHosts = this.getUncachedHosts(names);
// Resolve the uncached hosts
List<String> resolvedHosts = rawMapping.resolve(uncachedHosts);
this.cacheResolvedHosts(uncachedHosts, resolvedHosts);
return this.getCachedHosts(names);
}
先把你傳進來的東西統統強保證成ip的形式,然後看看這些ip是不是被cache住了,如果cache住了,直接就能從cache裡拿到這個機器的位置。
否則對於沒有cache的就呼叫rawMapping 的resolve方法,這個rawMapping追蹤發現是ScriptBasedMapping類的內部類RawScriptBasedMapping的例項,前面rawMapping 實際呼叫的resolve方法是:
public List<String> resolve(List<String> names) {
List <String> m = new ArrayList<String>(names.size());
if (names.isEmpty()) {
return m;
}
if (scriptName == null) {
for (int i = 0; i < names.size(); i++) {
m.add(NetworkTopology.DEFAULT_RACK);
}
return m;
}
String output = runResolveCommand(names);
if (output != null) {
StringTokenizer allSwitchInfo = new StringTokenizer(output);
while (allSwitchInfo.hasMoreTokens()) {
String switchInfo = allSwitchInfo.nextToken();
m.add(switchInfo);
}
if (m.size() != names.size()) {
// invalid number of entries returned by the script
LOG.warn("Script " + scriptName + " returned "
+ Integer.toString(m.size()) + " values when "
+ Integer.toString(names.size()) + " were expected.");
return null;
}
} else {
// an error occurred. return null to signify this.
// (exn was already logged in runResolveCommand)
return null;
}
return m;
}
這裡就比較簡單了,看起來是呼叫一個指令碼,然後執行這個指令碼,這個指令碼具有這樣的功能,輸入ip地址給它,它給你返回位置,我擦,這不就是讓管理員手工配置一個機器和機架的對映關係嗎!
this.scriptName = conf.get(SCRIPT_FILENAME_KEY);
static final String SCRIPT_FILENAME_KEY = "topology.script.file.name";
配置檔案中key “topology.script.file.name”指定的指令碼的位置,這個指令碼要有可執行許可權,完成上面的功能。
if (scriptName == null) {
for (int i = 0; i < names.size(); i++) {
m.add(NetworkTopology.DEFAULT_RACK);
}
return m;
}
如果你沒有配置這個東西,那麼所有的節點都會在同一個機架上,即"/default-rack"。
好了,那麼我們看一下這個指令碼該怎麼配置。