基於源碼分析hadoop namenode格式化和啟動過程實現 (According to the source code analysis hadoop namenode formatting and startup process implementation.)
Namenode 管理hdfs元數據和RPC服務響應客戶端,初次使用時需格式化;元數據存儲在edits和fsimage文件,其中 fsimage 保存最新的元數據信息,edits 保存自最新的元數據信息后的變化;edits 存儲的信息會在下一次啟動 namenode 時與 fsimage 合並產生新的 fsimage 文件。我們先以1.0版本源碼為例介紹,后期再補充新版本的擴展和改變,由於篇幅原因,部分代碼被去除。
1 namenode 格式化
初次啟動namenode時,hadoop要求必須格式化namenode;
hadoop namenode -format
格式化會刪除namenode image和edits文件,再創建新的 image 和 edits;
public static void format(File dir, Configuration conf)
throws IOException {
File image = new File(dir, "image");
File edits = new File(dir, "edits");
if (!((!image.exists() || FileUtil.fullyDelete(image, conf)) &&
(!edits.exists() || edits.delete()) &&image.mkdirs())) {
throw new IOException("Unable to format: "+dir);
}
}
所以已經生產使用的集群,謹慎使用。
2 namenode初始化和啟動
Configuration conf = new Configuration();
NameNode namenode = new NameNode(conf);
namenode.join();
namenode初始化和啟動過程中四個重要的操作:
1、根據(fs.default.name(舊版本參數)/fs.defaultFS(新版本參數))配置的主機名和端口號創建套接字地址,這個地址就是 namenode 文件系統元數據地址; 2、加載 namenode 元數據;首先將現有的 image 數據以層級方式加載到內存列表,加載完成后存放到活動列表中;
TreeSet activeBlocks = new TreeSet();
DataInputStream in = new DataInputStream(new BufferedInputStream(new FileInputStream(curFile)));
try {
int numFiles = in.readInt();
for (int i = 0; i < numFiles; i++) { UTF8 name = new UTF8(); name.readFields(in); int numBlocks = in.readInt(); if (numBlocks == 0) { unprotectedAddFile(name, null); } else { Block blocks[] = new Block[numBlocks]; for (int j = 0; j < numBlocks; j++) { blocks[j] = new Block(); blocks[j].readFields(in); } unprotectedAddFile(name, blocks); } } } finally { in.close(); }
對於存儲了大量數據的集群,元數據加載到內存會比較耗時,總的元數據數 / 加載完成后存放到活動列表 的數量 * 100% 就是我們在前端界面看到的namenode啟動進度。image 加載完成后,繼續加載最新的 edits ,並將 edits 合並到 image 產生新的 image 文件,並創建一個新的 edits 文件記錄系統運行時的變化。
public FSDirectory(File dir) throws IOException {
File fullimage = new File(dir, "image");
if (! fullimage.exists()) {
throw new IOException("NameNode not formatted: " + dir);
}
File edits = new File(dir, "edits");
if (loadFSImage(fullimage, edits)) {
saveFSImage(fullimage, edits);
}
synchronized (this) {
this.ready = true;
this.notifyAll();
this.editlog = new DataOutputStream(new FileOutputStream(edits));
}
}
3、創建datanode節點和namenode心跳監控、創建租憑管理監控(可以理解為鎖管理,比如釋放鎖);
this.hbthread = new Daemon(new HeartbeatMonitor());
this.lmthread = new Daemon(new LeaseMonitor());
hbthread.start();
lmthread.start();
namenode通過間隔的檢查與datanode心跳來判斷datanode的存活;租憑管理監控通俗的理解就是當某個操作(比如:寫數據)超出namenode規定的最大操作時間時,這個租憑管理監控進程就會認為該操作過期了,將它釋放清除。
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;
}
}
4、為namenode創建RPC服務器(線程池),這個線程池的作用是用來處理客戶端的遠程過程調用及集群守護進程的調用;
public NameNode(Configuration conf) throws IOException {
this(getDir(conf),DataNode.createSocketAddr(conf.get("fs.default.name", "local")).getPort(), conf);
}
public NameNode(File dir, int port, Configuration conf) throws IOException {
this.namesystem = new FSNamesystem(dir, conf);
this.handlerCount = conf.getInt("dfs.namenode.handler.count", 10);
this.server = RPC.getServer(this, port, handlerCount, false, conf);
this.server.start();
}
通過dfs.namenode.handler.count 參數設置線程池大小,默認是10;
public synchronized void start() throws IOException {
Listener listener = new Listener();
listener.start();
for (int i = 0; i < handlerCount; i++) { Handler handler = new Handler(i); handler.start(); } }
在生產環境中,該參數的設置需要多方面考慮和商榷,對於大集群,來自不同DataNode的並發心跳以及客戶端並發的元數據操作可能巨大,如果線程池太小,總是忙碌狀態,客戶端連接NameNode的時候總是超時或者連接被拒絕,這意味着需要更大的池來處理;線程池太大,會引發調用延遲和增加主機負載等問題;
this.server = RPC.getServer(this, port, handlerCount, false, conf);
this.server.start();
到此RPC服務器(線程池)構建並啟動,整個namenode啟動完成,RPC服務器(線程池)進入監聽響應狀態;
210116 222524 Server listener on port 9000: starting
210116 222524 Server handler 0 on 9000: starting
210116 222524 Server handler 1 on 9000: starting
210116 222524 Server handler 2 on 9000: starting
210116 222524 Server handler 3 on 9000: starting
210116 222524 Server handler 4 on 9000: starting
210116 222524 Server handler 5 on 9000: starting
210116 222524 Server handler 6 on 9000: starting
210116 222524 Server handler 7 on 9000: starting
210116 222524 Server handler 8 on 9000: starting
210116 222524 Server handler 9 on 9000: starting
3 總結
Namenode 格式化就是刪除已經存在的edits和fsimage文件,創建新的;Namenode啟動時創建套接字地址、加載 namenode 元數據、創建監控進程(datanode節點和namenode心跳監控、租憑管理監控)、創建RPC服務器(線程池)。
參考文獻
- https://hadoop.apache.org/docs/r1.0.4 - hadoop documenation