hadoop中重要的组成部分HDFS,它所发挥的重要作用是进行文件的后端存储。HDFS针对的是低端的服务器,场景为读操作多、写操作少的情况。在分布式存储情况下,比较容易出现的情况是数据的损害,为了保证数据的可靠和完整,实现了数据的检验和(checksum)和多副本放置策略等措施。在HDFS中采用比较多的是CRC(循环冗余校验码)的检验和方式。HDFS是一种基于块的分布式文件系统,其他的有基于对象的分布式文件系统Ceph。
下面介绍几个关于HDFS中写入操作涉及到的概念:
1、Block:在HDFS中,每个文件都是采用的分块的方式存储,每个block放在不同的datanode上,每个block的标识是一个三元组(block id, numBytes,generationStamp),其中block id是具有唯一性,具体分配是由namenode节点设置,然后再由datanode上建立block文件,同时建立对应block meta文件(其中的内容,大部分是对应数据的检验和内容)。
2、packet:在DFSclient与DataNode之间通信的过程中,发送和接受数据过程都是以一个packet为基础的方式进行。
3、Chunk:对应的中文名字也可以称为块,但是为了与block区分,还是称之为chunk。在DFSClient与DataNode之间通信的过程中,由于文件采用的是基于块的方式来进行的,但是在发送数据的过程中是以packet的方式来进行的,每个packet包含了多个chunk,同时对于每个chunk进行checksum计算,生成checksum bytes。
下面是Packet的数据格式:
+-----------------------------------------------------------------------+
| 4 byte packet length(exclude packet header) |
+-----------------------------------------------------------------------+
| 8 byte offset in the block | 8 byte sequence number |
+-----------------------------------------------------------------------+
| 1 byte isLastPacketInBlock |
+-----------------------------------------------------------------------+
| 4 byte Length of actual data |
+-----------------------------------------------------------------------+
| x byte checksum data. x is defined below |
+-----------------------------------------------------------------------+
| actual data ......... |
+-----------------------------------------------------------------------+
x = (length of data + BYTE_PER_CHECKSUM - 1) / BYTE_PER_CHECKSUM * CHECKSUM_SIZE
在DFSClient与DataNode之间通信采用的是基于C/S的结构。其中涉及到DFSClient中写入操作过程中的重要的类有FSDataOutputStream、DFSOutputStream、FSOutputSummer。
下面是关于DFSClient写入数据过程的分析:
1、建立DFSClient类型,然后通过clientprotocol的远程过程调用方法addblock,获取具体的块信息,返回给DFSClient
2、通过返回的locatedBlock信息建立pipleline在多个DataNode上
+------------+ connect +------------+ connect +------------+DFSClient | |---------->| |----------->| |-------> | datanode | ack | datanode | ack | datanode |<------- | |<----------| |<-----------| |ack +------------+ +------------+ +------------+
DFSClient在要发送数据包之前需要建立与多个Datanode之间的联系,这样在Pipeline建立之后就可以接下来的数据传输
3、DFSClient进行发送数据使用的是FSOutputSummer类的write(byte[ ]buffer, int offset,int len),当达到一个chunk的时候调用DFSOutputStream的writechunk(byte[] b, int offset,int len,byte[] checksum),当达到一个packet的时候就完成了这个packet,接下来就是进入到发送队列中去,由DataStreamer类对象的发送线程发送数据(其中建立与多个datanode之间的数据pipeline的联系是由DataStreamer完成,当一个Block完成后,开始建立另外一个pipeline,重复上述过程)
附加说明:在上述DFSClient与DataNode之间建立联系的过程中发送的请求报文头部和DFSClient与DataNode之间建立读请求的报文头部差不多类似。在此不多加说明。
有了DFSClient,对应需要有DataNode上对应的守护线程来进行数据的接收,下面是DataNode上的接收过程分析:
1、DataNode上启动了一个重要的线程DataXceiverServer,用于侦听来自DFSClient或者其他DataNode上的连接请求
2、当DFSClient需要进行连接的时候,DataXceiverServer响应请求,建立一个DataXceiver线程,为之服务,接下来就是建立pipeline的连接管道,验证发送过来的请求报文头部信息
3、如果接受到的请求是OP_WRITE_BLOCK,则建立BlockReceiver类对象,用于接受具体的data,同时也接受来自数据中的checksum,两部分不同的数据放在不同的部分,data部分数据放在标准的block文件中,而checksum数据放在meta为后缀名的文件中保留下来,为以后数据完整性检查使用(可以是DFSClient进行验证,也可以是Blockscanner验证)