Hadoop-组件-HDFS-源码学习-元数据管理-文件目录树-创建目录(mkdirs)
一、概述
二、实现
一直点点点~~~,最终代码定位到 NameNodeRpcServer#mkdirs()
1 | // 此处调用服务端 RPC |
检查 NameNode 是否启动
1 | checkNNStartup(); |
namesystem.mkdirs
1 | return namesystem.mkdirs(src, new PermissionStatus(getRemoteUser().getShortUserName(), null, masked), createParent); |
检查权限
1
checkOperation(OperationCategory.WRITE);
检查是否是安全模式
判断是否是安全模式,如果是安全模式是不可以写入数据的
1
checkNameNodeSafeMode("Cannot create directory " + src);
创建文件夹
1
auditStat = FSDirMkdirOp.mkdirs(this, src, permissions, createParent);
1 | existing = createChildrenDirectories(fsd, existing, ancestors, addImplicitUwx(permissions, permissions)); |
2.1. 解析路径,得到 INode
2.1.1. 校验是否是有效路径(以/为开头)
1 | if (!DFSUtil.isValidName(src)) { |
2.1.2. 得到处理后的 src 路径
1 | src = fsd.resolvePath(pc, src, pathComponents); |
/usr/hive/warehose/data
2.1.3. 将当前路径解析成 INode 结构路径
1 | INodesInPath iip = fsd.getINodesInPath4Write(src); |
2.1.4. 获取上一个 INode
1 | final INode lastINode = iip.getLastINode(); |
现有路径:/user/hive/warehouse
需要创建:/user/hive/warehouse/data
上一个 INode 就是:warehouse
如果之前的节点是不存在的 /user/hive/warehouse
,那么 lastINode = /
2.1.5. 返回当前传入的 INode 结构
1 | INodesInPath existing = lastINode != null ? iip : iip.getExistingINodes(); |
2.1.6. 获取不存在的节点路径
1 | List<String> nonExisting = iip.getPath(existing.length(), iip.length() - existing.length()); |
2.2. 添加 INode
1 | existing = unprotectedMkdir(fsd, fsd.allocateNewInodeId(), existing, |
二、更新文件目录树
更新文件目录树,这颗目录树是存在于内存中,由 FSDirectory 管理
1 | existing = unprotectedMkdir(fsd, fsd.allocateNewInodeId(), existing, |
可能在其他地方也会出现,这个
unprotected
一般指的就是内存
2.1. 构建新增节点
如果是多重目录,则构建第一个不存在的节点
1 | final INodeDirectory dir = new INodeDirectory(inodeId, name, permission, timestamp); |
2.2. 添加节点到父目录
构建的节点添加到父目录下面,构成树结构
1 | INodesInPath iip = fsd.addLastINode(parent, dir, true); |
2.2.1. 获取父目录
1 | final INodeDirectory parent = existing.getINode(pos - 1).asDirectory(); |
2.2.2. 在父目录下添加解析后的 INode
在老版本的 HDFS 代码中,向 INodeDirectory 中添加、删除以及修改子目录项的操作只需要更改 INodeDirectory.children 字段即可。添加了快照机制之后,对于子目录项的修改都需要由 DirectoryWithSnapshotFeature 代理。
DirectoryWithSnapshotFeature 类用于存储和处理指定目录下建立的所有快照,也就是记录目录快照之间进行的添加、删除、修改目录项的操作。
DirectoryDiff、DireetoryDiffList 以及 ChildrenDiff 类都是 Direetory WithSnapshotFeature 类的内部类。
1 | added = parent.addChild(inode, true, existing.getLatestSnapshotId()); |
判断添加的子目录项是否己经在当前的 INodeDirectory
addChild()方法首先判断添加的子目录项是否己经在当前的 INodeDirectory 中
1
2
3
4final int low = searchChildren(node.getLocalNameBytes());
if (low >= 0) {
return false;
}判断当前 INodeDirectory 对象是否在最新的快照中
然后判断当前 INodeDirectory 对象是否在最新的快照中,如果是则获取 DireetoryWithSnapshotFeature 对象,并给 INodeDirectory 添加 DirectoryWithSnapshotFeature 特性,之后 INodeDirectory 中所有对于 children 字段的修改方法都会由 DirectoryWithSnapshotFeature 中对应的方法代理,比如 addChild()、removeChild()方法等。
如果不在最新的快照中,则直接调用 INodeDirectory 的 addChild() 方法,将 INode 节点加入 INodeDirecotry的 children 集合中。
1
2
3
4
5
6
7
8if (isInLatestSnapshot(latestSnapshotId)) {
DirectoryWithSnapshotFeature sf = this.getDirectoryWithSnapshotFeature();
if (sf == null) {
sf = this.addSnapshotFeature(null);
}
return sf.addChild(this, node, setModTime, latestSnapshotId);
}
addChild(node, low);获取 DireetoryWithSnapshotFeature 对象,然后在这个对象上调用 adaChild()方法,DirectoryWithSnapshotFeature 的 addChild() 方法首先从 DirectoryDiffList 中获取当前快照对应的 DirectoryDiff 对象,这个对象用于保存快照建立之后对目录的添加、删除、修改子目录项等操作。
获取 ChildrenDiff对象
DirectoryDiff 对象会将当前目录中新创建的子目录项放入 DirectoryDiff.childrenDiff 字段的 c-list 中,而将删除的子目录项放入DirectoryDiff.childrenDiff 字段的 d-list 中
1
ChildrenDiff diff = diffs.checkAndAddLatestSnapshotDiff(latestSnapshotId,parent).diff;
添加 INode 到 ChildrenDiff 的 c-list
之后 ChildrenDiff.create()方法将这个新添加的 INode 对象放入 ChildrenDiff 的 c-list中。成功在 DirectoryDiff 对象中记录修改操作后,DirectoryWithSnapshotFeature 的代理工作就完成了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16private void insert(final ListType type, final E element, final int i) {
List<E> list = type == ListType.CREATED? created: deleted;
if (i >= 0) {
throw new AssertionError("Element already exists: element=" + element
+ ", " + type + "=" + list);
}
if (list == null) {
list = new ArrayList<E>(DEFAULT_ARRAY_INITIAL_CAPACITY);
if (type == ListType.CREATED) {
created = list;
} else if (type == ListType.DELETED){
deleted = list;
}
}
list.add(-i - 1, element);
}INode 对象放入INodeDirectory. children 字段
最后 addChild()方法会调用 INodeDirectory.add()方法,将新创建的 INode 对象放入INodeDirectory. children 字段中。
更新父目录的修改时间
1
2
3if (setModTime) {
updateModificationTime(node.getModificationTime(), latestSnapshotId);
}
到这儿 Namenode 内存中文件系统目录树更新就完成了~~~