HDFS架构师 4- DataNode写数据流程详细分析

UNIT4

image.png

1. 创建INodeFile流程分析 0:15:00 ~ 0:29.19

2步:
FileSystem.create

DistributedFileSystem.create
——》DFSClient.create
——》DFSOutputStream.newStreamForCreate
——》dfsClient.namenode.create(远程代理类)
↓ 远程
NamenodeRpcserver.create

namesystem.startFile
—》FSNamesystem:startFileInt
—》FSNamesystem: startFileInternal

iip = dir.addFile //添加文件
leaseManager.addLease //添加租约

2. 添加契约流程分析 0:29.19 ~ 0:36.51

,因为HDFS上的文件是不允许并发写的,比如并发的追加一些数据什么。 同一时间只能有一个客户端获取NameNode上面一个文件的契约,然后才可以向获取契约的文件写 入数据。 此时如果其他客户端尝试获取文件契约的时候,就获取不到,只能干等着。 通过这个机制,可以保证同一时间只有一个客户端在写一个文件。 在获取到了文件契约之后,在写文件的过程期间,那个客户端需要开启一个线程,不停的发送请求给 NameNode进行文件续约,告诉NameNode: 我还在写文件啊,你给我一直保留那个契约好吗? 而NameNode内部有一个专门的后台线程,负责监控各个契约的续约时间。 如果某个契约很长时间没续约了,此时就自动过期掉这个契约,让别的客户端来写。

接上 leaseManager.addLease 租约
—》 leases.put sortedLeases.add 可排序

image.png

3. DataStreamer启动流程分析 0:36:51 ~ 0:52:29

接上回 1 DFSOutputStream.newStreamForCreate 方法
—》DataStreamer#start() -- run方法

//dataqueue里面没有数据,代码阻塞在此,有数据则notify
dataQueue.wait(timeout);

image.png

4. 启动文件续约流程分析 52:29 ~ 1:00:00

退回 DFSClient.create 分析 beginFileLease 开启续约
/* 这个方法完成3件事 :

  • 1、往文件目录里面加了 文件 
    
  •  2、添加了租约 
    
  •   3、 启动了   DataStreamer流程 , dataqueue 有数据则notify 
    

*/
DFSClient#create
—》 beginFileLease() 开始续约
—》(LeaseManager)getLeaseRenewer().put
—》LeaseManager#run(final int id)
—》LeaseManager#renew
—》DFSClient#renewLease 57:17
↓ 远程RPC
—》 NamenodeRpcserver#renewLease
—》FSNamesystem#renewLease
—》LeaseManager#renewLease

逻辑如下

//先移除
sortedLeases.remove(lease);
//修改心跳     
 lease.renew();
//把改后的加入
      sortedLeases.add(lease);

5. 契约扫描机制分析 1:00:00 ~ 1:15:11

LeaseManager#Monitor#run
—》LeaseManager#checkLeases

try {
    //拿出最早的租约
     leaseToCheck = sortedLeases.first();
   } catch(NoSuchElementException e) {}

   while(leaseToCheck != null) {
     if (!leaseToCheck.expiredHardLimit()) {
       break;
     }

6. chunk封装为packet 写入DataQueue流程剖析 1:14:11~ 1:50:11

接上回 1
客户端代码为:
FileSystem.create()// 这个上面做过分析了
现在到
FSDataOutputStream.write

FSDataOutputStream.write()

HdfsDataOutputStream的父类FSDataOutputStream.write()

//TODO out 即为DFSOutputStream
out.write(b);

DFSOutputStream的父类FSOutputSummer.write()
—》FSOutputSummer#flushBuffer
—》FSOutputSummer#writeChecksumChunks

writeChunk(b, off + i, chunkLen, checksum, ckOffset, getChecksumSize());

——》子类DFSOutputStream#writeChunk
——》DFSOutputStream#writeChunkImpl
——》DFSOutputStream#waitAndQueueCurrentPacket
——》DFSOutputStream#queueCurrentPacket//把packet加入 list
——》DFSOutputStream# dataQueue.notifyAll();
——》DFSOutputStream#DataStreamer#run()

7. pipline数据管道流程之 namenode 申请Block 流程剖析 1:53:40 ~ 1:59:40

接上回 DFSOutputStream#DataStreamer#run 里面 这句setPipeline(nextBlockOutputStream());

这个方法 nextBlockOutputStream() 即为Block申请流程

DFSOutputStream#DataStreamer#nextBlockOutputStream
——》lb = locateFollowingBlock(excluded.length > 0 ? excluded : null);
return dfsClient.namenode.addBlock ;
↓ RPC 远程调用
——》 NameNodeRpcServer#addBlock

namesystem.getAdditionalBlock
——》FSnamesystem#getAdditionalBlock 选择存放datablock的主机-负载均衡,机架感知
1 saveAllocatedBlock 修改内存内的目录树,修改元数据
——》 dir.addBlock

getBlockManager().addBlockCollection //BlockManager记录block信息
fileINode.addBlock //新产生的block放到文件节点下
2 persistNewBlock(src, pendingFile); 元数据落盘

回到 setPipeline() 下面开始建立数据管道

8. pipline数据管道流程建立分析 2:00:00 ~

DFSOutputStream#DataStreamer#setPipeline() 方法
DFSOutputStream#DataStreamer#setPipeline
—》DFSOutputStream#DataStreamer#setPipeline#nextBlockOutputStream
—》DFSOutputStream#DataStreamer#createBlockOutputStream

out = new DataOutputStream(new BufferedOutputStream(unbufOut,
HdfsConstants.SMALL_BUFFER_SIZE))
new Sender(out).writeBlock
—》Sender#writeBlock()
—》Sender#send()
out.flush();
↓ RPC 远程调用
DataXceiverServer#run()

//TODO 接受socket请求
peer = peerServer.accept();
//每一个block都启动一个DataXceiver
new Daemon(datanode.threadGroup,
DataXceiver.create(peer, datanode, this))
.start();

DataXceiver.run()
—》 processOp()

case WRITE_BLOCK:
opWriteBlock(in);
—》 DataXceiver.writeBlock

9. 管道建立容错处理 (retry ,排除刚才问题机器) 2.25:00~ 2.30:00

接上 7 节
DFSOutputStream#DataStreamer#nextBlockOutputStream 方法

//TODO 整个管道建立是这段代码
success = createBlockOutputStream(nodes, storageTypes, 0L, false);
.....
if (!success) {
DFSClient.LOG.info("Abandoning " + block);
//管道不成功就 放弃block
dfsClient.namenode.abandonBlock(block.getCurrentBlock(),
..... 排除节点hadoop3
excludedNodes.put(nodes[errorIndex], nodes[errorIndex]);

image.png

.....
lb = locateFollowingBlock

10. ResponseProcessor组件初始化流程分析 2:31:00 ~ 2:41:00

接上回7: DFSOutputStream#DataStreamer#run 方法

DFSOutputStream#DataStreamer#setPipeline后面

//TODO ResponseProcessor组件初始化
initDataStreaming();

response = new ResponseProcessor(nodes);
response.start();

DFSOutputStream# ResponseProcessor.run()

//TODO
ackQueue.removeFirst();
dataQueue.notifyAll();

image.png

11. BlockReceiver和PacketResponder初始化 2:40:00~

接上回7: DFSOutputStream#DataStreamer#run 方法

// write out data to remote datanode
TraceScope writeScope = Trace.startSpan("writeTo", span);
try {
one.writeTo(blockStream);
blockStream.flush();

DataXceiverServer.run

new Daemon(datanode.threadGroup,
DataXceiver.create(peer, datanode, this)) .start();

DataXceiver.run
——》 DataXceiver.processOp()
——》DataXceiver.opWriteBlock()
——》DataXceiver.writeBlock()

blockReceiver = new BlockReceiver(block, storageType, in,
blockReceiver.receiveBlock
——》BlockReceiver.receiveBlock()

responder = new Daemon(datanode.threadGroup, new PacketResponder(replyOut, mirrIn, downstreams));

BlockReceiver.PacketResponder.run()

removeAckHead()

ackQueue.removeFirst();
ackQueue.notifyAll();

12. 写数据层层上报处理结果 3.13

13. 写数据容错分析 3.20

你可能感兴趣的:(HDFS架构师 4- DataNode写数据流程详细分析)