在对hbase有了一点了解后最近开始学习hadoop的相关源码,首先看了下某高人的hadoop源码分析系列http://caibinbupt.iteye.com/blog/monthblog/2008-11,看了三十几篇停了,有些看不懂,虽然代码解释的比较细但类实在太多,想一下子理清思路比较难。果断使用学习hbase时的方法,把hadoop最原始的化石版本0.1.0搞来看看,本文将介绍namenode的重要逻辑实现。
namenode初始化流程:
1.初始化FSNamespace(处理hadoop中虚拟文件系统的大多逻辑)
2.初始化配置
FSNamespace初始化
1。初始化FSDirectory
2.初始化HeartbeatMonitor,管理datanode的心跳,超时则删除此节点
3.初始化LeaseMonitor,管理hadoop的契约,会定期处理超时的操作
FSDirectory初始化:
1.loadFSImage 从image load 文件名=》blockid的map
2.saveFSImage 把1的map持久化到文件
3.this.editlog = new DataOutputStream 打开editlog的输出流,准备随时写入
0.1.0版本果然是非常的简单,namenode在启动后啥也不干,只是接受RPC请求,所以后面会以重要函数为切入介绍namenode。
FSDirectory.LoadFsImage:
作用:我们知道namenode需要维护filename-》blocksequence的map,LoadFsImage就是从image文件中load 该map
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);
}
}
解读:
1.很有意思,image的读写中全是直接的readField和writeField来做持久化,没有任何读写object
2.numBlocks为零即为目录
3.Block对象包括blockId,length
synchronized (rootDir) {
if (blocks != null) {
// Add file->block mapping
for (int i = 0; i < blocks.length; i++) {
activeBlocks.add(blocks[i]);
}
}
return (rootDir.addNode(name.toString(), blocks) != null);
}
执行rootDir.addNode添加该node,包括文件名和对应的blocks。
解释下INode类:
类似Linux中INode的概念,看下INode的成员变量
class INode :
public String name;
public INode parent;
public TreeMap children = new TreeMap();
public Block blocks[];
包含了文件名,父亲节点,孩子节点,和文件实际的blocks
再来看addNode方法:
INode addNode(String target, Block blks[]) {
if (getNode(target) != null) {
return null;
} else {
String parentName = DFSFile.getDFSParent(target);
if (parentName == null) {
return null;
}
INode parentNode = getNode(parentName);
if (parentNode == null) {
return null;
} else {
String targetName = new File(target).getName();
INode newItem = new INode(targetName, parentNode, blks);
parentNode.children.put(targetName, newItem);
return newItem;
}
}
}
1.首先尝试getNode,如果取到INode对象表示已经有该节点,返回失败
2.DFSFile.getDFSParent拿到parent文件夹的名字,方法是简单的字符串操作
3.拿到parent INode
4.创新新INode,并添加到parent中。这边调用了 new File().getName,没太明白,应该不会真的创建文件的,只是调用下getName方法。
再看下getNode方法:
Vector components = new Vector();
int start = 0;
int slashid = 0;
while (start < target.length() && (slashid = target.indexOf('/', start)) >= 0) {
components.add(target.substring(start, slashid));
start = slashid + 1;
}
if (start < target.length()) {
components.add(target.substring(start));
}
return getNode(components, 0);
根据'/'符把文件路径分割成一个个component,然后调用getNode(components,0)递归获取INode。
INode getNode(Vector components, int index) {
if (! name.equals((String) components.elementAt(index))) {
return null;
}
if (index == components.size()-1) {
return this;
}
// Check with children
INode child = (INode) children.get(components.elementAt(index+1));
if (child == null) {
return null;
} else {
return child.getNode(components, index+1);
}
}
这样,通过调用LoadFsImage方法就成功将Image中的数据转换为Inode对象挂到rootNode上了。
Namenode.open:
方法申明:public LocatedBlock[] open(String src)
作用:client从namenode打开一个文件
LocatedBlock包括了该文件需要的blocks和每个block对应的datanode的集合,在实际读取过程中每个block会随即从拥有该block的datanode中读出block。
public LocatedBlock[] open(String src) throws IOException {
Object openResults[] = namesystem.open(new UTF8(src));
if (openResults == null) {
throw new IOException("Cannot open filename " + src);
} else {
Block blocks[] = (Block[]) openResults[0];
DatanodeInfo sets[][] = (DatanodeInfo[][]) openResults[1];
LocatedBlock results[] = new LocatedBlock[blocks.length];
for (int i = 0; i < blocks.length; i++) {
results[i] = new LocatedBlock(blocks[i], sets[i]);
}
return results;
}
}
namesystem.open
public Object[] open(UTF8 src) {
Object results[] = null;
Block blocks[] = dir.getFile(src);
if (blocks != null) {
results = new Object[2];
DatanodeInfo machineSets[][] = new DatanodeInfo[blocks.length][];
for (int i = 0; i < blocks.length; i++) {
TreeSet containingNodes = (TreeSet) blocksMap.get(blocks[i]);
if (containingNodes == null) {
machineSets[i] = new DatanodeInfo[0];
} else {
machineSets[i] = new DatanodeInfo[containingNodes.size()];
int j = 0;
for (Iterator it = containingNodes.iterator(); it.hasNext(); j++) {
machineSets[i][j] = (DatanodeInfo) it.next();
}
}
}
results[0] = blocks;
results[1] = machineSets;
}
return results;
}
首先通过dir.getFile拿到该文件对应的blocks列表,然后从block->datanode的map中取出相应的machineSets。
namenode的主要逻辑我个人感觉搞清楚file->blockseqid和blockid->machineSet这2个map就差不多了。后面有空会分析下最新的hadoop2.0的代码。
是时候看点真正的代码了!。。。