HDFS基础知识、集群搭建及JavaAPI使用
HDFS基础知识、集群搭建及JavaAPI使用
- 基础知识
- 1简介
全称Hadoop Distributed File System,是Hadoop的两大核心技术之一。
- 2分布式文件系统结构
主节点承担起数据目录、元数据服务,从节点承担具体存储的任务。
- 3HDFS实现目标
兼容廉价的硬件设备
实现流数据读写
支持大数据集
支持简单的文件模型
强大的跨平台兼容性
- 4HDFS的局限性
不适合低延迟的数据访问(HBase可以满足实时处理需求)
无法高效存储大量小文件(如果小文件过多,HDFS的索引结构将会相当庞大,搜索效率会越来越低)
不支持多用户写入及任意修改文件(只允许追加不允许修改)
- 5HDFS的相关概念
块:目的是为了分摊磁盘读写开销,也就是在大量数据间分摊磁盘寻址开销。HDFS的一个块要比普通文件系统的块大很多,普通一块为64MB也可以设置成128MB,这样设计的目的是支持面向大规模数据存储,降低分布式节点的寻址开销。
HDFS两大组件:
名称节点:是整个HDFS集群的管家,负责整个系统元数据的管理,记录信息,相当于数据目录。
数据节点(DateNode):存储实际数据。
名称节点中保存的元数据:
里面包含以下信息:
文件是什么
文件被分成多少块
每个块和文件是怎么映射的
每个块被存储在哪个服务器上
- 6HDFS存储原理
冗余数据存储的好处:加快数据传输速度,很容易检查数据错误(有参照),保证了数据可靠性
HDFS会选择空间比较大,不太忙的数据节点存放副本
数据读取(就近选择)
HDFS提供了一个API可以确定一个数据节点所属的机架ID,客户端可以调用API来获取自己所属机架的ID
当客户端读取数据时,从名称节点获得数据库不同副本的存放位置列表,列表中包含了副本所在的数据节点,可以调用API来确定客户端和这些数据节点所属的机架ID,当发现某个数据块副本对应的机架ID和客户端对应的机架ID相同时就优先选择该副本读取数据,如果没有发现,就随机选择一个副本读取数据。
数据的错误与恢复
名称节点出错:暂停服务一段时间,从第二名称节点恢复数据(HDFS2.0是热备份,不需要暂停服务时间)
数据节点: 数据节点每个一段时间会给名称节点发送心跳信息,如果一段时间名称节点没有收到心跳信息,则认为数据节点出现故障。则复制备份信息。
- 7HDFS读数据过程
第一步:打开文件
第二步:获取数据块信息(通过ClientProtocal.getBlockLocations()查找下一个数据块)
第三步:读取请求(选择距离客户端最近的数据节点读数据)
第四步:读取数据
第五步:获取数据块信息(通过ClientProtocal.getBlockLocations()查找下一个数据块)
第六步:读取数据
第七步:关闭文件
- 8HDFS写数据过程
- Shell命令访问文件系统
- HDFS集群搭建
第一步:搭建虚拟机
首先,准备3台虚拟机,其中 1 台虚拟机作为NameNode,2台虚拟机作为DataNode,执行命令:vim /etc/hosts。
第二步:配置Hadoop环境
在linux上配置Hadoop环境
配置完环境后输入source /etc/profile命令,使环境变量生效。
第三步:设置SSH免密登录
设置 SSH 免密码登录,由于master机器将成为Hadoop 集群的NameNode节点,因此配置其可以免密登录集群中其它的slave机器。
执行命令:ssh-****** -t rsa
执行命令后,出现提示可以不予理会,直接按几次回车键就可以了。当出现以下界面时,则说明生成私钥id_rsa和公钥id_rsa.pub成功:
接下来,把生成的公钥id发送到slave1、slave2。
执行命令:ssh-copy-id slave1
slave1会要求你输入slave1这台机器上的密码,密码输入正确后,它已经添加了**。输入 SSH 命令测试 slave1 的免密登陆。
执行命令:ssh slave1
出现下图说明免密登录成功。
第四步:配置HDFS
在所有机器上的/hadoop-2.7.3/etc/hadoop目录中,修改core-site.xml和hdfs-site.xml文件,以完成 HDFS 的配置。
具体配置可参见:https://www.cnblogs.com/yjt1993/p/9505145.html
第五步:格式化 NameNode 以及启动 HDFS 系统
在master这台机器上,输入命令 HDFS 格式化命令。
执行命令:hdfs namenode –format
格式化完成之后,输入 HDFS 系统启动命令。
执行命令:start-dfs.sh
接下来,检查 HDFS 是否启动成功。在游览器中输入如:http://192.168.56.101:50070/,默认为NameNode的IP + 50070端口,当见到以下界面的时候,就说明集群已经起来了。
- 利用Java API与HDFS进行交互
Pom依赖:
- <!--hadoop相关依赖 -->
- <dependency>
- <groupId>org.apache.hadoop</groupId>
- <artifactId>hadoop-client</artifactId>
- <version>2.6.0</version>
- </dependency>
- <dependency>
- <groupId>org.apache.hadoop</groupId>
- <artifactId>hadoop-hdfs</artifactId>
- <version>2.6.0</version>
- </dependency>
- <dependency>
- <groupId>org.apache.hadoop</groupId>
- <artifactId>hadoop-common</artifactId>
- <version>2.6.0</version>
- </dependency>
JavaAPI:
- /**
- 静态代码块,设置HDFS访问路径
- */
- static{
- conf.set("fs.defaultFS", HDFS_PATH);
- }
- /**
- * 查看目录下所有文件
- * @author zhouyuhuai
- * @param filepath为目录路径
- */
- public static FileStatus[] getFilesList(String filePath) {
- try {
- FileSystem fs = FileSystem.get(conf);
- FileStatus[] fileStatus = fs.listStatus(new Path(filePath));
- fs.close();
- return fileStatus;
- } catch (Exception e){
- e.printStackTrace();
- return null;
- }
- }
- /**
- * 上传本地文件
- * @author zhouyuhuai
- * @param src 本地文件路径
- */
- public static List uploadFile(String src) throws IOException{
- FileSystem fs = FileSystem.get(conf);
- List filePath = new ArrayList();
- //本地上传文件路径
- Path srcPath = new Path(src);
- //HDFS目标路径
- Path dstPath = new Path(dst);
- if(!fs.exists(dstPath)){
- fs.mkdirs(dstPath);
- }
- fs.copyFromLocalFile(srcPath, dstPath);
- //返回文件路径
- FileStatus [] fileStatus = fs.listStatus(dstPath);
- for (FileStatus file : fileStatus)
- {
- filePath.add(file.getPath());
- }
- fs.close();
- return filePath;
- }
- /**
- * 下载文件至本地
- * @author zhouyuhuai
- * @param dst HDFS文件路径
- * @param src 下载目标位置路径
- * */
- public static void downloadFromHdfs(String dst,String src) throws IOException{
- FileSystem fs = FileSystem.get(conf);
- Path path=new Path(dst);
- InputStream in = fs.open(path);
- OutputStream out = new FileOutputStream(src);
- IOUtils.copyBytes(in, out, 4096, true);
- }
- /**
- * 删除文件
- * @author zhouyuhuai
- * @param dst HDFS文件路径
- * */
- public static boolean delete(String dst){
- try{
- FileSystem fs = FileSystem.get(conf);
- Path path = new Path(dst);
- boolean isok = fs.deleteOnExit(path);
- fs.close();
- return isok;
- }catch (Exception e){
- e.printStackTrace();
- }
- return false;
- }
- /**
- * 通过浏览器上传文件
- * @param in
- * @param hdfsPath
- */
- public static boolean uploadFromBrowser(InputStream in, String hdfsPath) {
- FileSystem fs = null;
- try {
- fs = FileSystem.get(conf);
- } catch (IOException e) {
- e.printStackTrace();
- return false;
- }
- FSDataOutputStream out = null;
- try {
- out = fs.create(new Path(hdfsPath));
- } catch (IOException e) {
- e.printStackTrace();
- return false;
- }
- try {
- IOUtils.copyBytes(in, out, conf);
- } catch (IOException e) {
- e.printStackTrace();
- }
- return true;
- }
- /**
- * 通过浏览器下载
- * @param path 下载文件路径
- * @param response
- * @return
- */
- public static boolean downLoadFromBrowser(String path, HttpServletResponse response){
- FileSystem fs = null;
- try {
- fs = FileSystem.get(conf);
- InputStream in = fs.open(new Path(path));
- String name = path.substring(path.lastIndexOf("/")+1);
- //设置content-disposition响应头控制浏览器以下载的形式打开文件
- response.setHeader("content-disposition", "attachment;filename=" + name);
- byte[] buffer = new byte[1024];
- //通过response对象获取OutputStream流
- OutputStream out = response.getOutputStream();
- int len = 0;
- //将FileInputStream流写入到buffer缓冲区
- while ((len = in.read(buffer)) > 0) {
- //使用OutputStream将缓冲区的数据输出到客户端浏览器
- out.write(buffer, 0, len);
- }
- in.close();
- } catch (IOException e) {
- e.printStackTrace();
- return false;
- }
- return true;
- }