DataXceiver写数据的过程详解

在上篇文章中,已经介绍了DataXceiver读取数据的详细过程。这篇文章就讲解一下流式接口向数据节点写数据的操作,DataTransferProtocol.write()方法给出了写操作的接口定义,操作码是80,DataXceiver.writeblock()则真正实现了DataTransferProtocol.writeblock()方法。
我们知道,HDFS使用数据流管道来写数据,DFSClient通过调用Sender.writeblock()方法触发一个写数据块请求,这个请求会传送到数据流管道中的每一个节点,数据流管道中的最后一个数据节点会回复请求确认,这个确认消息会逆向的通过数据流管道送回DFSClient。DFSclient收到请求确认后,将要写入的数据块切分成若干个数据包,然后依次先数据流管道发送这些数据包。数据包首先会从DFSClient发送到数据流管道中的第一个数据节点(Datanode1),Datanode1成功的接收到数据包后,会将数据包写入磁盘,然后将数据包发送到数据流管道的第二个节点(Datanode2),依次类推,当数据包到达数据流管道的最后一个数据节点时(datanode3),datanode3会对接收到的数据包进行校验,如果校验成功,Datanode3会发送数据包确认消息,这个确认消息会逆向的通过数据流管道送回DFSClient。当一个数据块中的所有数据包都成功发送完毕,并且受到确认消息后,并且受到确认消息后,DFSClient会发送一个空的数据包标识当前数据块发送完毕。至此整个数据块发送流程结束。
这个写数据请求通过流失接口到达Datanode之后,Datanode上监听流式接口请求的DataXceiverServer会接收到这个请求,并构造一个DataXceiver对象,然后在DataXceiver对象上调用DataXceiver.writeBlock方法响应这个请求。当前Datanode的DataXceiver.writeBlock方法会级联的向数据流管道中的下一个Datanode发送写数据块请求,这个流式请求会一直在数据流管道中传递下去,直到写数据块请求到达数据流管道中的最后一个datanode.

BlockReceiver类
writeBlock()方法下的BlockReceiver类负责从数据流管道中的上游接收数据块,然后保存数据块到当前数据节点的存储中,再将数据块转发到数据流管道中的下游数据节点。同时Blockreceiver还会接收来自下游节点的响应,并发这个响应发送给数据流管道中的上游节点、。
BlockReceiver.receiverBlock()方法比较简单,它先启动packetResponder线程负责接收并转发下游数据节点发送的确认数据包的ACK消息。之后,receiverBlock方法循环调用receiverpacket()方法接收上游写入的数据包并发送这个数据包到下游节点,成功完成整个数据包的写入操作后,receiverBlock方法关闭Packetresponder线程。

receivepacket()方法
receivepacket()方法首选首先会从输入流中屈辱一个数据包,并将这个数据包放在缓冲区中,receivePacket()成功接收数据包之后,会判断当前节点是否是数据流管道中的最后一个节点,或者输入流是否启动了同步数据块标识,要求Datanode立即将数据包同步到磁盘。在这两种情况下,datanode会先将数据写入磁盘,然后再通知packetResponder处理确认消息,否则,reiceivepacket方法接收完数据包后会立即通知packetResponder处理确认消息。
接下来reiceiPacket()会将数据包发送给数据流管道中的下游节点,然后就可以将数据块文件和校验文件写入数据节点的磁盘,如果当前节点时数据流管道中的最后一个节点,则在写入磁盘前,需要对数据包进行校验。

BlockReceiver.PacketResponder
已知BlockReceiver负责从上游数据节点接收数据包并转发到下游数据节点。同时如果当前节点是数据流管道中的最后一个节点,BlockReceiver还需要验证数据包的校验和是否正确。除此之外,数据节点还需要从下游节点接收数据包的确认消息,然后转发给上游节点,BlockReceiver将这部分功能委托给内部类Packetresponder。
PacketResponder是一个独立的线程类,它和Blockreceiver所在的线程共同完成数据块的写操作流程。BlockReceiver完成对指定数据包的处理之后,会触发Packetresponder类处理当前数据包的响应消息,PacketResponder监听下游的输入流,接收到这个数据包的确认消息之后,在确认信息中,添加当前数据节点的消息,然后将这个消息发送给上游数据节点。

BlockResponder完成对指定的数据包的处理之后,会调用PacketResponder类处理这个数据包的响应。首先会将等待PacketResponder类处理的数据包加入到ackQueue队列中。然后会调用notify()方法通知run()方法处理数据包。可以看到,ackQueue是一个典型的生产者-消费者队列。
完成上述操作之后,packetResponder会判断当前接收的数据包的响应是否为数据块中最后一个数据包的响应,如果是,则调用finalizeBlock()方法向namenode提交这个数据块,,完成数据包的响应处理之后,从ackQueue队列中移除这个数据包。

你可能感兴趣的:(HDFS源码)