hadoop学习笔记(三)HDFS读写过程

数据存放存放策略

HDFS采用了多副本的冗余存储,通常把一个Block的多个副本分别存储到不同的数据结点上,默认情况下HDFS默认的副本是3,也就是冗余因子为3,每一个block被保存到三个地方,一般伪分布式的冗余因子为1,因为只有一台datanode的机器,一般的存放策略就是,两份副本放在一个rack上,另一个放在不同的rack上。

默认的副本存放策略

HDFS体系结构

整个HDFS的体系结构如下:
在这里插入图片描述

读写指定用户

hdfs和linux文件系统类似也是有用户,用户组来管理的。一般来说,在shell命令中可以创建用户,hdfs也会根据linux用户创建用户

在代码中,无论是读或者写,都可以指定用户。Java客户端会先读取JVM系统的变量“HADOOP_USER_NAME”来作为用户名。

System.setProperty("HADOOP_USER_NAME","root");

读数据过程

在这里插入图片描述

首先要了解客户端,DistributedFileSystem类 ,FSDataInputStream类和FIleSystem类

客户端有shell,web,API提供调用,其底层都是java程序。

  • FIleSystem类是一个抽象基类,基本上所有的HDFS的操作都在这个类中,属于HDFS的API
    在这里插入图片描述
  • DistributedFileSystem类是在客户端实现的了FIleSystem,是hdfs客户端程序中的实现,并不属于hdfs的API,属于客户端程序的具体实现。

例如:

DistributedFileSystem extends FileSystem ...{
....
}
  • FSDataInputStream,站在程序的角度看待输入输出,读取数据当然是通过文件系统输入进程序了(本质是输入到缓存),这个类中还包括DFSnputStream类,这个类是FSDataInputStream的一部分,主要负责HDFS内部的通信也就是NameNode和DataNode之间的通信,虽然途中没画出来但是也要知道,在这里客户端本身就在NameNode和DataNode结点上。

有点类似于这样

public FSDataInputStream open(....)  {

  return new DFSClient.DFSInputStream(
          ....
        }
}

客户端读过程:

  1. DistributedFileSystem.open(…), DistributedFileSystem实现了FileSystem,也是调用了FileSystem的open()。(对应图中 1)
  2. 之后就是创建FSDataInputStream来和NameNode进行通信(本质是DFSInputStream,只不过FSDataInputStream用这个类做成了API),获取文件开始部分元数据信息,对其Block对应的DataNode的地址进行排序,返回给客户端。(对应图中 2)
  3. 输入流FSDataInputStream(本质上是DFSInputStream),客户端就开始读取,根据排序的结果按序建立连接,接连读取数据。(对应图中3,4) , 对部分数据读取完毕之后,关闭与各个NameNode的连接。
  4. 如果没有读完,再次找到文件的下一个数据块的NameNode的位置信息,如果期间之内,已经获取了元数据在缓存中,就不用寻找。(对应图中的5,6),读取完毕之后,关闭客户端与DataNode的连接,然后一直循环,直到所有数据块读取完毕。
  5. 最后读取完毕之后,关闭输入流(对应7)

如果与DataNode通信错误,那么就连接block存储的下一个DataNode。

自己用java实现读过程,一般需要自己添加jar包:
(1)”/usr/local/hadoop/share/hadoop/common”目录下的hadoop-common-2.x.jar和haoop-nfs-2.x.jar;
(2)/usr/local/hadoop/share/hadoop/common/lib”目录下的所有JAR包;
(3)“/usr/local/hadoop/share/hadoop/hdfs”目录下的haoop-hdfs-2.x.ar和haoop-hdfs-nfs-2.2.x.jar;
(4)“/usr/local/hadoop/share/hadoop/hdfs/lib”目录下的所有JAR包。

具体过程:

  1. 通过的配置类获取FileSystem操作对象
  2. open获取输入流对象
  3. 通过本地的输入流显示
import java.io.BufferedReader;
import java.io.InputStreamReader;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.FSDataInputStream;
 
public class HDFSFileExist{
        public static void main(String[] args) {
                try {
                        Configuration conf = new Configuration();
                        conf.set("fs.defaultFS","hdfs://localhost:9000"); // 通过此类设置NameNode的值                 
                        FileSystem fs = FileSystem.get(conf);		// 通过配置类实现FileSystem
                        Path file = new Path("test");  // HDFS中的user路径,也可以加默认的绝对路径。
                        FSDataInputStream getIt = fs.open(file);  // 打开输入流,创建了输入流对象
                        BufferedReader d = new BufferedReader(new InputStreamReader(getIt));
                        String content = d.readLine(); //读取文件一行
                        System.out.println(content);
                        d.close(); //关闭文件
                        fs.close(); //关闭hdfs
                } catch (Exception e) {
                        e.printStackTrace();
                }
        }
}

写数据过程

在这里插入图片描述

读过程涉及到的类和写过程涉及到的类类似,不同的是读写过程。

客户端写过程:

  1. FileSystem.create()创建出到HDFS的输出流,具体在客户端里,是DistributedFileSystem.create()创建出FSDataOutputStream,其中包含了DFSOutputStream是实际和NameNode通信的类,都和读取类似。(对应图中1)
  2. DistributedFileSystem通过的一种RPC 的技术远程调用NameNode这个进程,在文件系统命名空间中创建一个新文件,NameNode执行检查,看是否符合文件已经存在,权限检查等等,全部符合之后,添加元数据信息和相关的文件信息。(对应图中2)
  3. 获得输出流FSDataOutputStream对象之后(实际上是DFSOutputStream中的内部队列存放了块的队列),选择合适的DataNode,客户端开始write(),采用了流水线复制策略(对应图中3,4):
    (1)输出流会向NameNode提出申请,申请要保存block的若干结点,这些结点之间就会打开一个数据通道
    (2)通过输出流向第一个DataNode发送Block,当写入本地达到4KB的同时,申请第二个DataNode发送连接请求并把第一个DataNode
    收到的4KB数据发送给第二DataNode,以此类推直到Block写完。
  4. 传送完毕之后,为了确认收到的block是准确的,收到block的DataNode要向发送者发送确认包,顺着数据通道逆流发送,直到客户端收到应答的时候,将该block从队列中移除(对应图中5)。
  5. 不断执行3-5过程,直到文件全部写完
  6. 客户端关闭输出流,然后就不会写入数据了,内部队列都收到确认之后,关闭RPC远程调用NameNode,通知NameNode写入成功。

自己使用java程序写入数据如下:

import org.apache.hadoop.conf.Configuration;  
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.Path;
 
public class HDFSFileExist {    
        public static void main(String[] args) { 
                try {
                        Configuration conf = new Configuration();  
                        conf.set("fs.defaultFS","hdfs://localhost:9000");                      
                        FileSystem fs = FileSystem.get(conf);	// 获取配置信息,创建FileSystem对象
                        byte[] buff = "This is write file operating".getBytes(); // 要写入的内容
                        String filename = "/user/hadoop/test1.txt"; //要写入的文件名
                        FSDataOutputStream os = fs.create(new Path(filename));	// FileSystem创建输出流对象
                        os.write(buff,0,buff.length);
                        System.out.println("Create:"+ filename);
                        os.close();
                        fs.close();
                } catch (Exception e) {  
                        e.printStackTrace();  
                }  
        }  
}

经过运行发现,自己编写的java程序,可以重复用一个文件名,并且新写入的同名文件内容会替换掉上一次同名文件的内容,类似于替换。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值