文章目录
一、 HDFS 的IO操作
1.1 上传文件
```
@Test
public void testUploadFile() throws Exception {
FileInputStream fis = new FileInputStream("D:" + File.separator + "Hadoop_Test.txt");
FSDataOutputStream fos = fs.create(new Path("/user/kino/idea/Hadoop_Test"));
IOUtils.copyBytes(fis, fos, conf);
IOUtils.closeStream(fos);
IOUtils.closeStream(fis);
}
```
1.2 文件下载(完整下载)
@Test
public void testDownloadFile() throws Exception {
/**
* Path: HDFS 文件系统上的文件路径;
* bufferSize: 要使用的缓冲区的大小, 并不是要将文件下载多大, 默认 4K;
*/
FSDataInputStream fis = fs.open(new Path("/user/kino/input/hadoop-2.7.2.tar.gz"));
FileOutputStream fos = new FileOutputStream("D:\\hadoop-2.7.2.tar1.gz");
IOUtils.copyBytes(fis, fos, conf);
IOUtils.closeStream(fos);
IOUtils.closeStream(fis);
}
1.3 文件下载(分块(Block)下载)
读取大文件(超过 128M 的文件)
- 读取第一块(Block)
@Test public void testDownloadFileToBlockOne() throws Exception { /** * Path: HDFS 文件系统上的文件路径; * bufferSize: 要使用的缓冲区的大小, 并不是要将文件下载多大, 默认 4K; */ FSDataInputStream fis = fs.open(new Path("/user/kino/input/hadoop-2.7.2.tar.gz")); FileOutputStream fos = new FileOutputStream("D:\\hadoop-2.7.2.tar1.gz"); byte[] b = new byte[1024]; for (int i = 0; i < 1024*128; i++) { fis.read(b); fos.write(b); } IOUtils.closeStream(fos); IOUtils.closeStream(fis); }
2. 读取第二块(Block)
@Test
public void testDownloadFileToBlockTwo() throws Exception {
/**
* Path: HDFS 文件系统上的文件路径;
* bufferSize: 要使用的缓冲区的大小, 并不是要将文件下载多大, 默认 4K;
*/
FSDataInputStream fis = fs.open(new Path("/user/kino/input/hadoop-2.7.2.tar.gz"));
FileOutputStream fos = new FileOutputStream("D:\\hadoop-2.7.2.tar2.gz");
fis.seek(1024*1024*128);
IOUtils.copyBytes(fis, fos, conf);
IOUtils.closeStream(fos);
IOUtils.closeStream(fis);
}
在 window 中 合并两个文件: type file1 >> file2
此时文件大小和 HDFS 系统上的文件大小一致, 修改名字成hadoop-2.7.2.tar.gz
后解压后查看
二、 HDFS 写数据流程
2.1 剖析文件写入
HDFS 写数据流程
- 客户端通过 Distributed FileSystem 向 NameNode 请求上传文件, NameNode 检查目标文件是否已存在, 父目录是否存在;
- 如果不存在, NameNode 返回可以上传; 如果已经存在, NameNode 返回不可以上传;
- 客户端请求上传第一块数据, 询问 NameNode可以上传到哪些DataNode 服务器上;
- NameNode 返回 3个 DateNode 节点, 分别为 dn1, dn2, dn3;
- 客户端通过 FSDateOutputStream模块请求 dn1 上传数据, dn1 收到请求会继续调用 dn2, 然后 dn2 调用 dn3, 将这个通信管道建立完成;
- dn1、dn2、dn3 逐级应答客户端;
- 客户端开始往dn1上传第一个Block(先从磁盘读取数据放到一个本地内存缓存),以Packet为单位,dn1收到一个Packet就会传给dn2,dn2传给dn3;dn1每传一个packet会放入一个应答队列等待应答。
- 当一个Block传输完成之后,客户端再次请求NameNode上传第二个Block的服务器。(重复执行3-7步)。
2.2 网络拓扑-节点距离计算
在HDFS写数据的过程中,NameNode会选择距离待上传数据最近距离的DataNode接收数据。那么这个最近距离怎么计算呢?
节点距离:两个节点到达最近的共同祖先的距离总和。
下图中机架中的每个节点相当于一台服务器,
例如,假设有数据中心d1机架r1中的节点n1。该节点可以表示为/d1/r1/n1。利用这种标记,这里给出四种距离描述,如下图所示。
2.3 机架感知(副本存储节点选择)
-
For the common case, when the replication factor is three, HDFS’s placement policy is to put one replica on one node in the local rack, another on a different node in the local rack, and the last on a different node in a different rack.
-
Hadoop 副本节点选择
思考: 为什么要这么存?
三、 HDFS 读数据流程
- 客户端通过Distributed FileSystem向NameNode请求下载文件,NameNode通过查询元数据,找到文件块所在的DataNode地址。
- 挑选一台DataNode(就近原则,然后随机)服务器,请求读取数据。
- DataNode开始传输数据给客户端(从磁盘里面读取数据输入流,以Packet为单位来做校验)。
- 客户端以Packet为单位接收,先在本地缓存,然后写入目标文件。