DFSOutputStream源码分析

当使用Java API往hdfs写入数据 , 最终都是使用DFSOutputStream来处理

以下是一段,往hdfs /user/test/bb8/u这个文件如果1k的数据 ,当执行到(1)的时候 , 创建出来的流就是DFSOutputStream的是一个实例 , 当执行到(2)写入数据的时候 ,其实最终调用的DFSOutputStream.write1(byte b[], int off, int len)方法, 缓存到DFSOutputStream内部变量的buf中去了 ,如果写入的数据超过了buf的大小就需要调用flushBuffer()方法; 这个方法跟(3)功能差不多 , 下面会详细分析具体干了什么 。

FileSystem fs = FileSystem.get(URI.create(uri) , conf);
Path path = new Path(uri + "test/bb8/u");
FSDataOutputStream outputStream = fs.create(path) ;  //(1)
for(int i = 0 ; i < 2 ; i++ ) {
    outputStream.write(new byte[512]);  //(2)
}
outputStream.hflush(); //(3)
outputStream.close();

客户端写入的数据都缓存在stream内部 ,然后被切分成packets , 每一个包的大小是64K 。 一个包由多个chunk组成 。 每一个chunk一般是512 bytes和关联的校验和 。以下是packet格式 。

1 => 33 bytes的头
2 => 1 - 127 个的checksum
3 => 如果checksum不满127个 , 那么有一部分空闲的部分
4 => 1- 127 个chunk
5 => 与3一样 ,chunk数不满127个 , 剩余空闲的部分

[_________CCCCCCCCC________________DDDDDDDDDDDDDDDD___________]
    1     ^   2   ^       3        ^       4       ^     5     
          |        checksumPos     dataStart       dataPos
          checksumStart

具体的切分逻辑在writeChecksumChunks方法中 ,

private void writeChecksumChunks(byte b[], int off, int len)
  throws IOException {
    //计算总的校验和 ,放到checksum字节数组中
    sum.calculateChunkedSums(b, off, len, checksum, 0);
    //顺序,每次取512个字节 =>sum.getBytesPerChecksum() ,调用writeChunk ,组合成packet 
    for (int i = 0; i < len; i += sum.getBytesPerChecksum()) {
      int chunkLen = Math.min(sum.getBytesPerChecksum(), len - i);
      int ckOffset = i / sum.getBytesPerChecksum() * getChecksumSize();
      writeChunk(b, off + i, chunkLen, checksum, ckOffset, getChecksumSize());
    }
  }

buf 切分组装成packet放入currentPacket , 当填充满127个chunk,currentPacket 就需要往dataQueue 队列中去,具体逻辑就是DFSOutputStream.waitAndQueueCurrentPacket方法实现 。如果dataQueue大小加上 ackQueue的大小比我们配置的writeMaxPackets大了 ,那么调用dataQueue.wait();先暂停往dataQueue加入packet ;否则调用DFSOutputStream.queueCurrentPacket 方法把当前的currentPacket 加入到dataQueue中去 。

dataQueue数据是由DataStreamer线程发送到datanode上去

待完成…

你可能感兴趣的:(Hadoop)