HDFS写流程
HDFS写流程步骤
创建文件
1、客户端调用DistributedFileSystem.create()方法Rpc方式(ClientProtocol.create())远程调用NameNode(NameNodeRpcServer)的create()方法在HDFS上创建文件。
2、NameNode将该事务操作保持到edits.log文件当中
3、NameNode.create()创建成功,DistrbutedFileSystem.create()返回给客户端DFSOutputStream对象
建立数据流管道
4、客户端调用DFSOutputStream.write()方法写入数据
5、DFSOutputStream会先调用CilentProtocol.addBlock()方法,向NameNode申请空的数据块
6、CilentProtocol.addBlock()返回LocatedBlock对象,内容包含了写入到DataNode的位置信息中
向数据管道pipeline中写入数据
7、客户端向数据管道中写入数据:将数据写入到一个校验块chunk中,一个chunk大小为516byte大小,其中前面512byte为写入的数据,后4byte为校验值(checksum,根据crc32算法进行校验)
8、当一个chunk写入完成,会保持到更大的数据块中packet(64KB)
9、当packet写满之后,会被发到dataqueue队列中
10、packet从队列中取出,通过pipeline写入到datanode中,如图,先到datanode1,然后再有datanode1将输入写入datanode2,datanode3
11、当一个packet在被读取完写入到datanode之后,packet存储到ackqueue校验列表中,等到pipeline返回packet的ack反馈
12、每个包都会有ack反馈,当datanode1,datanode2,datanode3写入完成,会从datanode3->datanode2->datanode1逆反向返回ack校验值
13、如果ackqueue中的ack校验成功,则表示,这个packet写入完成,将该packet从ackqueue中移除,
如果校验失败,则表示ack校验失败,该packet会被ackqueue中取出,重新存储到dataqueue头部,被重新发送
14、当当前block块被写入完成,则会继续调用addBlock,步骤同上
15、当文件中最后一个block数据写入完成,会发送一个空的packet,表示写入结束,关闭pipeline
16、但文件写入完成,则客户端会调用clientProtocol.complete()通知namenode当前文件写入完成
代码演示:
import java.io.*;
import java.net.URI;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils;
public class copyLocalToHDFS {
public static void main(String[] args) {
//文件读取流
InputStream in = null;
try {
//读取数据到内存中
in = new BufferedInputStream(new FileInputStream(args[0]));
//创建配置文件对象
Configuration conf = new Configuration();
//创建文件系统
FileSystem fileSystem = FileSystem.get(URI.create(args[1]),conf);
//获取输出流,写入到HDFS
FSDataOutputStream fsDataOutputStream = fileSystem.create(new Path(args[1]));
//拷贝输入流到输出流,并关闭两个流
IOUtils.copyBytes(in,fsDataOutputStream,4096,true);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
HDFS写入流程容错处理
背景:在文件写入的过程中,有datanode出现网络故障(网络不同)
处理步骤:
1、ackqueue中的缓存数据会被重新载入到dataqueue中
2、输入流会调用ClientProtocol.updateBlockForPipeline(),为block申请一个新的时间戳,namenode会记录新的时间戳
3、当出现故障的datanode恢复,会上报block信息到namenode,如果存在block的时间戳与namenode中记录的不同,则namenode会通过返回心跳协议,让datanode删除本地的block
4、故障的datanode会被从pipeline中删除
5、输出流调用ClientProtocol.getAdditionalDatanode()通知namenode分配新的datanode到pipeline中,并使用新的时间戳建立pipeline
6、输出流调用ClientProtocol.updatePipeline(),更新namenode的元数据
7、HDFS客户端调用DataTransferProtocol通知pipeline中一个datanode,将block数据复制到新的datanode中
8、恢复故障,继续写入数据
HDFS读流程
1、客户端调用DistributedFileSystem.open()方法,远程调用namenode的open方法打开文件,返回FSDataInputStream对象
2、在构造FSDataInputStream对象时,namenode会调用getBlockLocations方法,获得文件的存储block块的datanode列表,(客户端在读取的时候,会根据网络拓扑,来读取文件)
3、客户端在调用FSDataInputStream的read方法,会在在里client最近的datanode,依此读取block块
4、当文件读取完成,则会调用FSDataInputStream的close方法
代码演示
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URI;
public class copyHDFSToLocal {
public static void main(String[] args) {
try {
//配置文件
Configuration conf = new Configuration();
FileSystem fileSystem = FileSystem.get(URI.create(args[0]), conf);
//获取HDFS输入流
FSDataInputStream open = fileSystem.open(new Path(args[0]));
//本地输出流
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(args[1]));
IOUtils.copyBytes(open,bufferedOutputStream,4096,true);
} catch (IOException e) {
e.printStackTrace();
}
}
}
HDFS读取流程容错处理
情况一:
在读取过程中,client与datanode的通信中断。client与此block的第二个datanode建立连接,读取。在本次读取中,会记录有问题的datanode,并在本次读取时,不会再他上读取数据
情况二:
client读取block数据时,会同时读取block的校验和,如果读取来的数据,计算校验和,如果该block数据的校验和和读取来的校验和不一样,则说明该block数据损坏。client则会再该block的副本上读取数据。同时client会告知namenode这个损坏的block。