HDFS文件读取:
- 数据读取的步骤:
- 客户端通过FileSystem对象(抽象类,一般通过这个来调用方法),open()方法打开需要读取的文件,HDFS中用DistributedFileSystem实现FileSystem的open()方法,通过远程调用(RPC)调用namenode,namenode告诉它存放该块复本的datanode地址
- datanode通过网络拓扑找到距离客户端最近的datanode并排序
- open()方法返回一个FSDataInputStream对象,客户端对FSDataInputStream的read()用来找到对应的最佳datanode,通过反复调用read()将结果告诉给客户端,到达块的末端时断开datanode之间的连接,再去找下一块的最佳datanode
- 最后,客户端调用FSDataInputStream close()完成数据读取
- 客户端读取HDFS文件系统中的数据图:
- 用API写个简单的文件读取例子
@Test
public void openTest() throws Exception {
FSDataInputStream in = fileSystem.open(new Path("/hdfsapi/test/aa.txt"));
//讲读出的数据输出到控制台上
IOUtils.copyBytes(in,System.out,1024);
//关闭数据流
IOUtils.closeStream(in);
}
Hadoop中的网络距离:
默认情况下,所有节点再同一数据中心的同一机架上。
图中数据中心是date center,机架是rack,节点是node,d代表两结点之间的网络距离。
1)同一节点上的进程: d(/dc1/rack1/node1 , /dc1/rack1/node1) = 0
2)同一机架上的不同节点: d(/dc1/rack1/node1 , /dc1/rack1/node2) = 2
3)同一数据中心的不同机架上的节点: d(/dc1/rack1/node1 , /dc1/rack2/node3) = 4
4)不同数据中心的节点: d(/dc1/rack1/node1 , /dc2/rack3/node4) = 6
- 读取数据过程出现错误的处理:
如果在读取数据过程中DFSInputStream(封装的FSDataInputStream,负责namenode和datanode之间的通信)与datanode通信时遇到错误,会从这个块的另一个最邻近datanode读取数据,同时记住故障datanode。DFSInputStream会是试图从其他datanode读取其复本,也将损坏块通知给namenode。
文件写入
- 数据写入的步骤:
- 客户端通过FileSystem对象(抽象类),DistributedFileSystem对象实现的create()方法新建文件,新建文件时需要确认客户端有权限以及文件是否已经存在,如果存在就会返回IOException异常
- 创建文件成功后返回FSDataOutputStream对象,客户端通过调用write()向datanode执行写入操作,连接DataNode时DFSOutputStream(FSDataOutputStream封装后,负责namenode和datanode之间的通信)。把数据分为一个个数据包并写入内部队列,叫“数据队列”(data queue),这一步由DataStreamer完成,它还负责选出合适存储数据复本的一组datanode,并告诉namenode分配所需的新的数据块
- 假设复本系数为3,则一个管线中有三个节点,DataStreamer将数据包流式传输到管线中第一个datanode,datanode存储数据包并将它发送到管线中的第二个datanode,按照同样的方法发送到最后一个datanode中
- 最后一个datanode再向前一个datanode发送确认回执,叫“确认队列”(ack queue)并讲确认队列返回给DFSOutputStream,直至收到所有datanode确认回执
- 客户端调用FSDataInputStream close(),完成数据写入
- 再完成数据写入前,namenode需要确认(complete)文件写入已完成,等待数据块最小量的复制
- 文件写入的例子
@Test
public void outputTest() throws Exception {
InputStream input = new BufferedInputStream(new FileInputStream(new File("E:\\ComputerQQDownload\\jdk-8u192-linux-x64.tar.gz")));
FSDataOutputStream out = fileSystem.create(new Path("/hdfsapi/test/jdk.tgz"), new Progressable() {
@Override
public void progress() {
System.out.print(">");
}
});
IOUtils.copyBytes(input, out, 4096);
out.close();
input.close();
}
复本怎么存
- 第一个复本时随机的存放再机架上的某一个节点
- 第二个复本是存放在同一数据中心的不同机架上的某一节点上,这个过程叫离架
- 第三个复本与第二个存放在同一机架上的某一节点上