話說client經過exists()函數得知目前的namenode那邊不存在此文件後, node
則經過namenode.create函數建立一個文件。具體細節以下: 數組
這裏意味着:clientMachine的clientName建立了src文件。 函數
clientMachine只用來選擇目標DataNode. 學習
public LocatedBlock create(String src, String clientName, String clientMachine, boolean overwrite) throws IOException { ui
Object results[] = namesystem.startFile(new UTF8(src), new UTF8(clientName), new UTF8(clientMachine), overwrite);//調用文件系統的startFile函數,返回值爲block信息和目標datanode信息 this
if (results == null) { spa
throw new IOException("Cannot create file " + src + " on client " + clientName); orm
} else { 內存
Block b = (Block) results[0];//取回block ci
DatanodeInfo targets[] = (DatanodeInfo[]) results[1];//獲取DatanodeInfo數組信息
return new LocatedBlock(b, targets);//組合返回最終信息
}
}
====================================
下面開始學習
public synchronized Object[] startFile(UTF8 src, UTF8 holder, UTF8 clientMachine, boolean overwrite) {
對此函數的分析以下:
public synchronized Object[] startFile(UTF8 src, UTF8 holder, UTF8 clientMachine, boolean overwrite) {
//背景知識:參數有holder和clientMachine.好比一個例子以下:
Holder:DFS_CLIENT_xxxx
clientMachine:Machine66.
也就是說一個clientMachine上面能夠有多個Holder.
一個clientMachine上的Holder發出了一個上傳的請求。
下面的代碼中哪裏用到了holder和哪裏用到了clientMachine,
還請讀者本身注意思考。
Object results[] = null;
if (pendingCreates.get(src) == null) {//說明pendingCreates記錄了正在建立的文件
boolean fileValid = dir.isValidToCreate(src);//文件路徑也確實不存在,須要這一句嗎?
if (overwrite && ! fileValid) {//若是能夠覆蓋的話,目前都是不能夠覆蓋
delete(src);
fileValid = true;
}
if (fileValid) {//確實能夠的話,繼續執行
results = new Object[2];//建立返回結果的數組
// Get the array of replication targets
DatanodeInfo targets[] = chooseTargets(this.desiredReplication, null, clientMachine);
//根據clientMachine和備份數目選擇多個目標datanode
if (targets.length < this.minReplication) {
LOG.warning("Target-length is " + targets.length +
", below MIN_REPLICATION (" + this.minReplication+ ")");
return null;
}//若是長度達不到備份數,則返回失敗
// Reserve space for this pending file
pendingCreates.put(src, new Vector());//代表這個文件正在create!!!
synchronized (leases) {//開始處理租約系統
Lease lease = (Lease) leases.get(holder);//查找租約系統
if (lease == null) {//若是不存在
lease = new Lease(holder);//建立
leases.put(holder, lease);//存儲到leases
sortedLeases.add(lease);//存儲到sortedLeases
} else {//若是存在的話,則lease自己刷新時間且從新加入到sortedLeases.
//注意,這裏有一個sort過程。
sortedLeases.remove(lease);
lease.renew();
sortedLeases.add(lease);
}
lease.startedCreate(src);//lease的自己creates保存了文件名
}
// Create next block
results[0] = allocateBlock(src);//主要是記錄文件對應的Block信息
results[1] = targets;//分配的datanode信息
} else { // ! fileValid
LOG.warning("Cannot start file because it is invalid. src=" + src);
}
} else {
LOG.warning("Cannot start file because pendingCreates is non-null. src=" + src);
}
return results;//返回結果!
}
-------------------------------------------------------------
DatanodeInfo[] chooseTargets(int desiredReplicates, TreeSet forbiddenNodes, UTF8 clientMachine) {
TreeSet alreadyChosen = new TreeSet();//初始化空的已經選擇的機器
Vector targets = new Vector();//真的無語。這裏爲啥還要再建立一個targets,浪費內存,直接傳到chooseTarget同樣的好吧!崩潰!
for (int i = 0; i < desiredReplicates; i++) {//根據備份數來選擇執行次數
DatanodeInfo target = chooseTarget(forbiddenNodes, alreadyChosen, clientMachine);//選擇單個機器
if (target != null) {//選擇好了一個,就加到targets和alreadyChosen.崩潰,加2次有啥意思!!!
targets.add(target);
alreadyChosen.add(target);
} else {
break; // calling chooseTarget again won't help
}
}
return (DatanodeInfo[]) targets.toArray(new DatanodeInfo[targets.size()]);//返回執行的結果
}
---------------
=======================
DatanodeInfo chooseTarget(TreeSet forbidden1, TreeSet forbidden2, UTF8 clientMachine) {
//
// Check if there are any available targets at all
//
int totalMachines = datanodeMap.size();//獲取當前已知的全部數據節點個數
if (totalMachines == 0) {//爲0就不用說了,返回null
LOG.warning("While choosing target, totalMachines is " + totalMachines);
return null;
}
//
// Build a map of forbidden hostnames from the two forbidden sets.
//
TreeSet forbiddenMachines = new TreeSet();
if (forbidden1 != null) {//這裏forbidden1是初始化禁止的節點,此處爲null
for (Iterator it = forbidden1.iterator(); it.hasNext(); ) {
DatanodeInfo cur = (DatanodeInfo) it.next();
forbiddenMachines.add(cur.getHost());
}
}
if (forbidden2 != null) {//是已經選擇的節點,由於已經選擇的就不會再返回了,你懂的
for (Iterator it = forbidden2.iterator(); it.hasNext(); ) {
DatanodeInfo cur = (DatanodeInfo) it.next();
forbiddenMachines.add(cur.getHost());
}
}
//
// Build list of machines we can actually choose from
//
Vector targetList = new Vector();//從總的節點中去掉不能夠選擇的節點,獲得剩下的可選的節點
for (Iterator it = datanodeMap.values().iterator(); it.hasNext(); ) {
DatanodeInfo node = (DatanodeInfo) it.next();
if (! forbiddenMachines.contains(node.getHost())) {
targetList.add(node);
}
}
Collections.shuffle(targetList);//原本不知道幹嗎的,百度了一下,用來洗牌的
//爲啥?由於DFSShell採用計算機組成原理的菊花鏈的方式來上傳數據。剩下的我就不用解釋了
//
// Now pick one
//
if (targetList.size() > 0) {//若是還剩下確實能夠選擇的節點,而且clientMachine也在裏面
//而且容量大於5塊,就直接返回clientMachine.我猜是爲了本地加速
//畢竟上傳到本地和上傳到遠程主機是不同的。
// If the requester's machine is in the targetList,
// and it's got the capacity, pick it.
//
if (clientMachine != null && clientMachine.getLength() > 0) {
for (Iterator it = targetList.iterator(); it.hasNext(); ) {
DatanodeInfo node = (DatanodeInfo) it.next();
if (clientMachine.equals(node.getHost())) {
if (node.getRemaining() > BLOCK_SIZE * MIN_BLOCKS_FOR_WRITE) {
return node;
}
}
}
}
//
// Otherwise, choose node according to target capacity
//不然,就從中選擇一個容量大於5塊的節點
for (Iterator it = targetList.iterator(); it.hasNext(); ) {
DatanodeInfo node = (DatanodeInfo) it.next();
if (node.getRemaining() > BLOCK_SIZE * MIN_BLOCKS_FOR_WRITE) {
return node;
}
}
//
// That should do the trick. But we might not be able
// to pick any node if the target was out of bytes. As
// a last resort, pick the first valid one we can find.
//不然,就選擇一個至少大於1塊的節點
for (Iterator it = targetList.iterator(); it.hasNext(); ) {
DatanodeInfo node = (DatanodeInfo) it.next();
if (node.getRemaining() > BLOCK_SIZE) {
return node;
}
}
LOG.warning("Could not find any nodes with sufficient capacity");
return null;//不然返回null
} else {
LOG.warning("Zero targets found, forbidden1.size=" +
( forbidden1 != null ? forbidden1.size() : 0 ) +
" forbidden2.size()=" +
( forbidden2 != null ? forbidden2.size() : 0 ));
return null;//一個可用來查找的節點都沒有!
}
}
好,建立文件及選擇Block的過程所有分析完畢!