内存文件目录树,在一些开源的分布式存储系统中,有时也叫做Namespace或者元数据。它是NameNode节点的一种记录客户端文件操作命令的抽象数据结构,维护在NameNode的自身内存中。
在上一章《dfs客户端》中,我提到NameNodeRpcServer在接受到Client的文件操作RPC请求后,会委托给 FSNameSystem 处理:
public class NameNodeServiceImpl extends NameNodeServiceGrpc.NameNodeServiceImplBase {
// 负责管理元数据的核心组件
private FSNameSystem namesystem;
/**
* 创建目录
*/
@Override
public void mkdir(MkDirRequest request, StreamObserver<MkDirResponse> responseObserver) {
try {
MkDirResponse response = null;
if (!isRunning) {
response = MkDirResponse.newBuilder().setStatus(STATUS_SHUTDOWN).build();
} else {
this.namesystem.mkdir(request.getPath());
response = MkDirResponse.newBuilder().setStatus(STATUS_SUCCESS).build();
}
responseObserver.onNext(response);
responseObserver.onCompleted();
} catch (Exception e) {
e.printStackTrace();
}
}
}
FSNameSystem 就是NameNode进行元数据管理的核心组件,本章,我就来实现FSNameSystem。
一、元数据管理
FSNameSystem内部包含了两个核心组件:
- FSDirectory:负责维护元数据,即内存文件目录树;
- FSEditlog:负责管理edits log日志。
/**
* 负责管理元数据的核心组件
*/
public class FSNameSystem {
// 负责管理内存文件目录树的组件
private FSDirectory directory;
// 负责管理edits log写入磁盘的组件
private FSEditlog editlog;
// 最近一次checkpoint更新到的txid
private long checkpointTxid = 0L;
public FSNameSystem() {
this.directory = new FSDirectory();
this.editlog = new FSEditlog();
recoverNamespace();
}
/**
* 创建目录
*
* @param path 目录路径
* @return 是否成功
*/
public Boolean mkdir(String path) throws Exception {
this.directory.mkdir(path);
this.editlog.logEdit("{'OP':'MKDIR','PATH':'" + path + "'}");
return true;
}
//...
}
可以看到,FSNameSystem把对内存文件目录树的操作委托给了FSDirectory
。
关于FSEditlog组件和edits log日志,我会在后续章节详细讲解,本章我们只关注内存文件目录树。
1.1 数据结构
我们需要先考虑下内存文件目录树的结构是怎么样的?内存文件目录树,本质是一个父子层级关系的数据结构,比如:
/root
/usr
/local
/app
/home
/kafka
/data
/access.log
对目录/文件进行增删改,本质都是在更新该内存里的数据结构,所以我们定义一个INode类,代表文件目录树中的一个节点:
/**
* 代表文件目录树中的一个目录
*/
public static class INode {
private String path;
private List<INode> children;
public INode() {
}
public INode(String path) {
this.path = path;
this.children = new LinkedList<INode>();
}
public void addChild(INode inode) {
this.children.add(inode);
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public List<INode> getChildren() {
return children;
}
public void setChildren(List<INode> children) {
this.children = children;
}
@Override
public String toString() {
return "INode [path=" + path + ", children=" + children + "]";
}
}
1.2 FSDirectory
定义完文件目录树的基本结构后,我们还需要一个组件 FSDirectory 对文件和目录进行封装,并暴露一些对外的接口。我这里只实现了mkdir
接口,你们可以根据业务需要实现各种其它维护文件树的接口:
/**
* 管理文件目录树的核心组件
*/
public class FSDirectory {
// 内存中的文件目录树
private INode dirTree;
public FSDirectory() {
// 初始化时只有根节点
this.dirTree = new INode("/");
}
/**
* 创建目录
*
* @param path 目录路径, eg: path = /usr/warehouse/hive
*/
public void mkdir(String path) {
synchronized (dirTree) {
// eg: /usr/warehouse/hive -> ["","usr","warehosue","hive"]
String[] pathes = path.split("/");
INode parent = dirTree;
for (String splitedPath : pathes) {
if (splitedPath.trim().equals("")) {
continue;
}
INode dir = findDirectory(parent, splitedPath);
if (dir != null) {
parent = dir;
continue;
}
INode child = new INode(splitedPath);
parent.addChild(child);
parent = child;
}
}
// printDirTree(dirTree, "");
}
/**
* 查找子目录
* <p>
* 在dir目录下查找子目录path
*/
private INode findDirectory(INode dir, String path) {
if (dir.getChildren().size() == 0) {
return null;
}
for (INode child : dir.getChildren()) {
if (child instanceof INode) {
INode childDir = (INode) child;
if ((childDir.getPath().equals(path))) {
return childDir;
}
}
}
return null;
}
public INode getDirTree() {
return dirTree;
}
public void setDirTree(INode dirTree) {
this.dirTree = dirTree;
}
}
上面需要特别注意的一点是,我使用了synchronized
对内存文件目录树进行加锁,以防止并发修改时出现问题。
二、总结
本章,我对NameNode中的文件目录树结构进行了讲解,同时给出了一个比较简单的实现,很多分布式文件系统的实现思想都是类似的。下一章开始,我将对最核心的edits log日志进行讲解。
Java 面试宝典是大明哥全力打造的 Java 精品面试题,它是一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅只是一道道面试题,而是一套完整的 Java 知识体系,一套你 Java 知识点的扫盲贴。
它的内容包括:
- 大厂真题:Java 面试宝典里面的题目都是最近几年的高频的大厂面试真题。
- 原创内容:Java 面试宝典内容全部都是大明哥原创,内容全面且通俗易懂,回答部分可以直接作为面试回答内容。
- 持续更新:一次购买,永久有效。大明哥会持续更新 3+ 年,累计更新 1000+,宝典会不断迭代更新,保证最新、最全面。
- 覆盖全面:本宝典累计更新 1000+,从 Java 入门到 Java 架构的高频面试题,实现 360° 全覆盖。
- 不止面试:内容包含面试题解析、内容详解、知识扩展,它不仅仅只是一份面试题,更是一套完整的 Java 知识体系。
- 宝典详情:https://www.yuque.com/chenssy/sike-java/xvlo920axlp7sf4k
- 宝典总览:https://www.yuque.com/chenssy/sike-java/yogsehzntzgp4ly1
- 宝典进展:https://www.yuque.com/chenssy/sike-java/en9ned7loo47z5aw
目前 Java 面试宝典累计更新 400+ 道,总字数 42w+。大明哥还在持续更新中,下图是大明哥在 2024-12 月份的更新情况:
想了解详情的小伙伴,扫描下面二维码加大明哥微信【daming091】咨询
同时,大明哥也整理一套目前市面最常见的热点面试题。微信搜[大明哥聊 Java]或扫描下方二维码关注大明哥的原创公众号[大明哥聊 Java] ,回复【面试题】 即可免费领取。