Namenode是hdfs的名字節點,保存着文件系統的元數據,響應客戶端與DataNode的請求。
有如下線程爲Namenode進行服務:
1、HeartbeatMonitor:心跳守護線程,該線程一直在進行與DataNode之間的心跳檢測,默認是EXPIRE_INTERVAL = 10 * 60 * 1000 (10分鐘)後,如果DataNode還沒有發來心跳信號,就進行如下處理:
1)將該DataNode從心跳列表中剔除;
2)文件系統總的容量也響應減少;
3)將該DataNode的死塊進行處理
synchronized void heartbeatCheck() {
synchronized (heartbeats) {
DatanodeInfo nodeInfo = null;
while ((heartbeats.size() > 0) &&
((nodeInfo = (DatanodeInfo) heartbeats.first()) != null) &&
(nodeInfo.lastUpdate() < System.currentTimeMillis() - EXPIRE_INTERVAL)) {
LOG.info("Lost heartbeat for " + nodeInfo.getName());
heartbeats.remove(nodeInfo);
synchronized (datanodeMap) {
datanodeMap.remove(nodeInfo.getName());
}
totalCapacity -= nodeInfo.getCapacity();
totalRemaining -= nodeInfo.getRemaining();
Block deadblocks[] = nodeInfo.getBlocks();
if (deadblocks != null) {
for (int i = 0; i < deadblocks.length; i++) {
removeStoredBlock(deadblocks[i], nodeInfo);
}
}
if (heartbeats.size() > 0) {
nodeInfo = (DatanodeInfo) heartbeats.first();
}
}
}
}
2)LeaseMonitor:租約守護線程,該線程一直進行租約的檢查;
class LeaseMonitor implements Runnable {
public void run() {
while (fsRunning) {
synchronized (FSNamesystem.this) {
synchronized (leases) {
Lease top;
while ((sortedLeases.size() > 0) &&
((top = (Lease) sortedLeases.first()) != null)) {
if (top.expired()) {
top.releaseLocks();
leases.remove(top.holder);
LOG.info("Removing lease " + top + ", leases remaining: " + sortedLeases.size());
if (!sortedLeases.remove(top)) {
LOG.info("Unknown failure trying to remove " + top + " from lease set.");
}
} else {
break;
}
}
}
}
try {
Thread.sleep(2000);
} catch (InterruptedException ie) {
}
}
}
}
3、Listener:Socket監聽線程,當監聽到一個請求時,創建一個服務端的Connection線程;
public void run() {
LOG.info(getName() + ": starting");
while (running) {
try {
new Connection(socket.accept()).start(); // start a new connection
} catch (SocketTimeoutException e) { // ignore timeouts
} catch (Exception e) { // log all other exceptions
LOG.log(Level.INFO, getName() + " caught: " + e, e);
}
}
try {
socket.close();
} catch (IOException e) {
LOG.info(getName() + ": e=" + e);
}
LOG.info(getName() + ": exiting");
}
4、Connection:服務端connection線程,做如下幾件事:
1)獲取調用參數param;
2)new一個call對象,並放入調用隊列callQueue;
3)喚醒調用隊列callQueue,交給後面的Handler去處理;
while (running) {
int id;
try {
id = in.readInt(); // try to read an id
} catch (SocketTimeoutException e) {
continue;
}
if (LOG.isLoggable(Level.FINE))
LOG.fine(getName() + " got #" + id);
Writable param = makeParam(); // read param
param.readFields(in);
Call call = new Call(id, param, this);
synchronized (callQueue) {
callQueue.addLast(call); // queue the call
callQueue.notify(); // wake up a waiting handler
}
while (running && callQueue.size() >= maxQueuedCalls) {
synchronized (callDequeued) { // queue is full
callDequeued.wait(timeout); // wait for a dequeue
}
}
}
5、Handler:處理請求隊列的守護線程,默認會起10個,當上述connection中請求隊列被喚醒時,對隊列中call真正進行處理;
Call call;
synchronized (callQueue) {
while (running && callQueue.size()==0) { // wait for a call
callQueue.wait(timeout);
}
if (!running) break;
call = (Call)callQueue.removeFirst(); // pop the queue
}
synchronized (callDequeued) { // tell others we've dequeued
callDequeued.notify();
}
if (LOG.isLoggable(Level.FINE))
LOG.fine(getName() + ": has #" + call.id + " from " +
call.connection.socket.getInetAddress().getHostAddress());
String error = null;
Writable value = null;
try {
value = call(call.param); // make the call
} catch (IOException e) {
LOG.log(Level.INFO, getName() + " call error: " + e, e);
error = getStackTrace(e);
} catch (Exception e) {
LOG.log(Level.INFO, getName() + " call error: " + e, e);
error = getStackTrace(e);
}
DataOutputStream out = call.connection.out;
synchronized (out) {
out.writeInt(call.id); // write call id
out.writeBoolean(error!=null); // write error flag
if (error != null)
value = new UTF8(error);
value.write(out); // write value
out.flush();
}