目录
HDFS
写数据流程
- 1)客户端这边先创建一个的RPC客户端,发送给NameNode进行请求,NameNode对这个请求进行判断,判断文件的路径是否存在,是否有权限添加
- 2)客户端创建一个
DataStreamer
,DataStreamer
里创建了一个队列dataQueue
,并将dataQueue
进行等待;同时DataStreamer
从NameNode
通过机架感知获取block
块可以写到哪些DataNode上 - 3)紧接着客户端会向DataNode请求建立数据传输通道
- 4)然后才真正开始写数据,先写
chunk
和checksum
,将其封装成packet,packet
要往dataQueue
里面写数据;会先查看dataQueue
里面是否已经满了,如果满了的话需要等待dataQueue
没满的时候再往里写;如果没满,就可以往dataQueue
写packet - 5)
packet
向dataQueue
里面已经写完之后会通知dataQueue
队列已经向队列里写完数据,接下来要把数据传到DataNode
- 6)由
DataStreamer
准备将dataQueue
的数据往DataNode
里发送,通过Socket的形式发送packet
- 发送packet时首先将
dataQueue
里对应的packet发送给DataNode一份 - 然后将
dataQueue
里对应的packet移除掉,再将其放到ackQueue
队列的尾部
- 发送packet时首先将
- 7)由
DataNode
对应的服务来接收对应的数据,接收完数据之后,先把数据持久化到磁盘,同时将数据发送到下一个节点- 下一个节点同样将数据持久化到磁盘,再往下一个节点发送,如此往复;
- 发送数据之后由后往前逐渐进行应答,直到应答到
ResponseProcessor
,ResponseProcessor
收齐三个应答(假设有三个DataNode)之后,会告诉ackQueue
当前应答已经全部收齐,ackQueue
可以将对应的packet数据删除;如果没有收齐三个应答,就会告诉ackQueue
队列保存,再将对应的packet移到dataQueue
尝试从新发送数据
create
进入到FileSystem
类下面的create
方法
public FSDataOutputStream create(Path f) throws IOException {
return create(f, true);
}
顺着create方法一直查看下去,找到如下代码,当前的create方法是抽象方法
public abstract FSDataOutputStream create(...) throws IOException;
回退到上一步,ctrl + alt + B
,查看实现类,以分布式文件系统为例,选择DistributedFileSystem
public FSDataOutputStream create(...) throws IOException {
return this.create(...);
}
继续跟进create方法
public FSDataOutputStream create(...) throws IOException {
return new FileSystemLinkResolver<FSDataOutputStream>() {
@Override
public FSDataOutputStream doCall(final Path p) throws IOException {
final DFSOutputStream dfsos = dfs.create(...);
return dfs.createWrappedOutputStream(dfsos, statistics);
}
}
跟进dfs.create()
public DFSOutputStream create(...)
throws IOException {
return create(src, permission, flag, true,
replication, blockSize, progress, buffersize, checksumOpt, null);
}
public DFSOutputStream create(...)
throws IOException {
return create(src, permission, flag, createParent, replication, blockSize,
progress, buffersize, checksumOpt, favoredNodes, null);
}
public DFSOutputStream create(...) throws IOException {
final DFSOutputStream result = DFSOutputStream.newStreamForCreate(...);
}
走到这里,看到dfsClient.namenode.create(...)
,代表客户端开始向namenode发送RPC请求,选中ctrl + alt + b
,可以看到NameNodeRpcServer
,是namenode的一个服务,现在相当于客户端跟服务端进行通信。接下来namenode就要检查目录树是否可以创建文件
static DFSOutputStream newStreamForCreate(...)
throws IOException {
stat = dfsClient.namenode.create(...);
}
选中NameNodeRpcServer
,进入到create方法
public HdfsFileStatus create(...) throws IOException {
status = namesystem.startFile(...);
}
跟进namesystem.startFile(...)
方法
HdfsFileStatus startFile(...) throws IOException {
status = startFileInt(...);
}
private HdfsFileStatus startFileInt(...) throws IOException {
stat = FSDirWriteFileOp.startFile(...);
}
会检查所要上传的目录是否存在,如果存在,并且传入的参数是可以覆盖的,则不抛出异常;否则会抛出异常;
static HdfsFileStatus startFile(...)
throws IOException {
if (overwrite) {
}
} else {
// If lease soft limit time is expired, recover the lease
throw new FileAlreadyExistsException(src + " for client " +
clientMachine + " already exists");
}
iip = addFile(...);
}
紧接着会执行addFile()
方法,跟进addFile()
方法
private static INodesInPath addFile(...) throws IOException {
// 创建一个文件
INodeFile newNode = newINodeFile(...);
// 执行addINode,添加目录树
newiip = fsd.addINode(existing, newNode, permissions.getPermission());
}
addINode
方法添加目录树,会根据以前的目录结构,选择一个节点往上面添加;至此,整个创建过程就结束
INodesInPath addINode(INodesInPath existing, INode child,
FsPermission modes)
throws QuotaExceededException, UnresolvedLinkException {
cacheName(child);
writeLock();
try {
return addLastINode(existing, child, modes, true);
} finally {
writeUnlock();
}
}
下面要进行的是准备工作,DataNode会准备一个上传队列,并将其状态设置为wait,阻塞等待,等待数据的上传。
回退到最开始,从create方法再逐步地跟进
FSDataOutputStream fos = fs.create(new Path("/input"));
public FSDataOutputStream create(Path f) throws IOException {
return create(f, true);
}
public FSDataOutputStream create(Path f, boolean overwrite)throws IOException {
return create(...);
}