Hadoop -- HDFS

1. 什么是Hadoop

hadoop中有3个核心组件:
分布式文件系统:HDFS —— 实现将文件分布式存储在很多的服务器上
分布式运算编程框架:MAPREDUCE —— 实现在很多机器上分布式并行运算
分布式资源调度平台:YARN —— 帮用户调度大量的mapreduce程序,并合理分配运算资源

2. HDFS的工作机制

在这里插入图片描述

  • HDFS对用户提供一个统一的目录树
  • 存储用户的文件时,会切成若干文件分布式地存储到Data Node服务器中,Data Node是一个软件,在存储文件时,是存在Data Node所运行的服务器的磁盘中;
  • 用户的文件可以存储多个副本,以增强数据的安全性(假如某一台服务器挂掉了,该服务器的文件块就会没了,但是还有其他副本存储在其他服务器上就很nice)
  • 用户文件块的存储位置信息,记录在Name Node软件中(那在Name Node中又是存在哪里呢?内存中,但是会定期序列化到磁盘中)

3. HDFS客户端编程

客户端有多种,例如命令行、页面、或者java程序,并且可以运行在任何一台机器上(HDFS集群中的一台机器或者一台独立的机器也可以)

3.1 命令行客户端常用操作命令

  1. 查看hdfs中的目录信息
hadoop fs -ls /hdfs
  1. 上传文件到hdfs中
hadoop fs -put /本地文件 /hdfs目标路径
hadoop fs -copyFromLocal /本地文件 /hdfs路径

## 跟copyFromLocal的区别是:从本地移动到hdfs中
hadoop fs -moveFromLocal /本地文件  /hdfs路径  
  1. 下载文件到客户端本地磁盘
hadoop fs -get /hdfs路径文件 /本地路径
hadoop fs -copyToLocal /hdfs路径文件 /本地路径
hadoop fs -moveToLocal /hdfs路径文件 /本地路径
  1. 在hdfs中创建文件夹
# -p 代表创建多层文件夹
hadoop fs -mkdir -p /aaa/xxx
  1. 移动hdfs中的文件(更名)
hadoop fs -mv /hdfs路径1 /hdfs路径2
  1. 删除hdfs中的文件或文件夹
# -r 代表删除整个文件夹
hadoop fs -rm -r /aaa
  1. 修改文件的权限
hadoop fs -chown user:group /aaa
hadoop fs -chmod 700 /aaa
  1. 追加内容到已存在的文件
hadoop fs -appendToFile /本地文件 /hdfs中的文件
  1. 显示文本文件的内容
hadoop fs -cat /hdfs中的文件
hadoop fs -tail /hdfs中的文件
  1. 启动命令

首先,初始化namenode的元数据目录
要在hdp-01上执行hadoop的一个命令来初始化namenode的元数据存储目录

hadoop namenode -format

然后,启动namenode进程和datanode(进入hadoop安装目录/sbin目录)

hadoop-daemon.sh start namenode
hadoop-daemon.sh start datanode

或者使用脚本批量启动

./start-dfs.sh
  1. 关闭命令
./stop-dfs.sh
  1. 退出hdfs安全模式
hdfs dfsadmin -safemode leave

3.2 java客户端编程

package HDFS;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Arrays;

import javax.imageio.stream.FileImageInputStream;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.LocatedFileStatus;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.RemoteIterator;
import org.junit.Before;
import org.junit.Test;

public class HdfsClientDemo {
	public static void main(String[] args) throws Exception {
		/**
		 * Configuration参数对象的机制:
		 * 	构造时,会加载jar包中的默认配置 xx-drfalut.xml
		 * 	再加载用户配置 xx-site.xml,覆盖掉默认参数
		 * 
		 * 	构造完成后,还可以conf.set("name","value"),会再次覆盖用户配置文件的参数值
		 */
		// new 时会从项目的classpath中加载core-defalut.xml hdfs-defalut.xml core-site.xml hdfs-site.xml等文件
		Configuration conf = new Configuration();
		
		// 指定客户端上传文件到hdfs时需要保存的副本数量
		conf.set("dfs.replication", "2");
		// 指定客户端上传文件到hdfs时切块的规格大小
		conf.set("dfs.blocksize","64m");
		FileSystem fs = FileSystem.get(new URI("hdfs://hadoop100:9000/"), conf, "root");
	
		fs.copyFromLocalFile(new Path("D:\\big data\\java\\EclipseKepler\\eclipse-jee-kepler-SR2-Java8-win32-x86_64.zip"), new Path("/"));
		fs.close();
	}
	
	FileSystem fs = null;
	@Before
	public void init() throws Exception{
		Configuration conf = new Configuration();
		conf.set("dfs.replication", "2");
		conf.set("dfs.blocksize","64m");
		fs = FileSystem.get(new URI("hdfs://hadoop100:9000/"), conf, "root");
	}
	
	/**
	 * 测试从服务器拷贝数据到本地磁盘
	 */
	@Test
	public void testGet() throws Exception{
		fs.copyToLocalFile(new Path("/eclipse-jee-kepler-SR2-Java8-win32-x86_64.zip"), new Path("e:/"));
		fs.close();
	}
	
	/**
	 * 在hdfs内部移动文件/重命名
	 */
	@Test
	public void testRename() throws Exception{
		fs.rename(new Path("/xxx.txt"), new Path("/y/x.txt"));
		fs.close();
	}
	
	/**
	 * 在hdfs创建文件夹
	 */
	@Test
	public void testMkdirs() throws Exception{
		fs.mkdirs(new Path("/xx/yy/zz"));
		fs.close();
	}
	
	/**
	 * 在hdfs删除文件夹
	 */
	@Test
	public void testDelete() throws Exception{
		fs.delete(new Path("/xx"),true);
		fs.close();
	}
	
	/**
	 * 查询hdfs指定目录下文件的信息
	 * (可以递归  所以返回一个迭代器  不然返回一个数组的话假如有很多文件,客户端所在的机器的内存也不够空间存放数据)
	 */
	@Test
	public void testLs() throws Exception{
		RemoteIterator<LocatedFileStatus> iter = fs.listFiles(new Path("/"), true);
		while(iter.hasNext()){
			LocatedFileStatus status = iter.next();
			System.out.println("文件全路径:"+status.getPath());
			System.out.println("文件块大小:"+status.getBlockSize());
			System.out.println("文件长度:"+status.getLen());
			System.out.println("副本数量:"+status.getReplication());
			System.out.println("文件信息:"+Arrays.toString(status.getBlockLocations()));
			System.out.println("-----------------------------------");
		}
		fs.close();
	}
	
	/**
	 * 查询hdfs指定目录下的文件和文件夹 (只能查看文件夹的下一级 不会递归)
	 */
	@Test
	public void testLs2() throws Exception{
		FileStatus[] listStatus = fs.listStatus(new Path("/"));
		
		for(FileStatus status:listStatus){
			System.out.println("文件全路径:"+status.getPath());
			System.out.println("文件块大小:"+status.getBlockSize());
			System.out.println("文件长度:"+status.getLen());
			System.out.println("副本数量:"+status.getReplication());
			
			System.out.println("-----------------------------------");
		}
		fs.close();
	}
	
	/**
	 * 读hdfs文件的内容
	 * 
	 */
	@Test
	public void testReadData() throws Exception{
		FSDataInputStream in = fs.open(new Path("/test.txt"));
		BufferedReader br = new BufferedReader(new InputStreamReader(in, "utf-8"));
		
		String line = null;
		while((line=br.readLine())!=null){
			System.out.println(line);
		}
		in.close();
		fs.close();
	}
	
	/**
	 * 读hdfs文件的指定偏移量范围的内容
	 * 
	 */
	@Test
	public void testRandomReadData() throws Exception{
		FSDataInputStream in = fs.open(new Path("/xx.dat"));
		
		// 将读取的起始位置进行指定
		in.seek(12);
		
		// 读16个字节
		byte[] buf = new byte[16];
		in.read(buf);
		
		System.out.println(new String(buf));
		
		in.close();
		fs.close();
	}
	
	/**
	 * 往hdfs中的文件写内容
	 */
	@Test
	public void testWriteData() throws Exception{
		//输出流 将文件写到hdfs中 路径:/xx.jpg
		FSDataOutputStream out = fs.create(new Path("/xx.hpg"), false);
		
		
		// 读本地的数据   本地文件路径:D:/....
		FileInputStream in = new FileInputStream("D:/.....");
		byte[] buf = new byte[1024];
		int read = 0;
		while((read=in.read(buf))!=-1){
			out.write(buf,0,read);
		}
		in.close();
		out.close();
		fs.close();
	}
}

4. 元数据管理机制

1、什么是元数据?
hdfs的目录结构及每一个文件的块信息(块的id,块的副本数量,块的存放位置)

2、元数据由谁负责管理?
namenode

3、管理机制示意图
在这里插入图片描述
在内存中,元数据的目录结构是树状的,会定期序列化(对象→二进制流)到磁盘中生成fsimage文件。但是假如内存中元数据非常大(几十G),客户端操作一次数据,即引起元数据变化,假如此时断电还没来得及更新到磁盘,怎么办?

这里当然不能采用 一旦元数据发生变化就序列化 的方法,因为元数据太大。namenode会把引起元数据变化的客户端操作记录在edits日志文件中,日志文件是有序切分的;假如断电了,则在下次启动时只需解析edits操作日志,在内存中更新元数据对象,然后再序列化到磁盘中就可以恢复最新的元数据,但是如果这样岂不是启动时间过长了?怎么办?

找一个秘书(Secondary namenode)定期来做这件事。 secondarynamenode会定期从namenode上下载fsimage镜像和新生成的edits日志,然后加载fsimage镜像到内存中,然后顺序解析edits文件,对内存中的元数据对象进行修改(整合)。整合完成后,将内存元数据序列化成一个新的fsimage,并将这个fsimage镜像文件上传给namenode。上述过程叫做:checkpoint操作

那secondary namenode每次做checkpoint操作时,都需要从namenode上下载上次的fsimage镜像文件吗?
第一次checkpoint需要下载,以后就不用下载了,因为自己的机器上就已经有了。

更详细文章:NameNode内存全景

5. 写数据到HDFS的流程

在这里插入图片描述
在这里插入图片描述

步骤解析:
(1)客户端通过Distributed FileSystem模块向NameNode请求上传文件,NameNode检查目标文件是否已存在,父目录是否存在。
(2)NameNode返回是否可以上传。
(3)客户端请求第一个 Block上传到哪几个DataNode服务器上。
(4)NameNode返回3个DataNode节点,分别为dn1、dn2、dn3。
(5)客户端通过FSDataOutputStream模块请求dn1上传数据,dn1收到请求会继续调用dn2,然后dn2调用dn3,将这个通信管道建立完成。(dn1为离自己最近的一台data node,怎样为最近?假如客户端离每个data node只有一台交换机(网关),则每个data node到客户端的距离是一致的,此时随机挑选;假如客户端到每台data node的距离不一致,即到某个data node之间经过多台交换机,则这个data node距离最远;当然还需考虑dn1的负载能力,假如有)
(6)dn1、dn2、dn3逐级应答客户端。
(7)客户端开始往dn1上传第一个Block(先从磁盘读取数据放到一个本地内存缓存),以Packet为单位,dn1收到一个Packet就会传给dn2,dn2传给dn3;dn1每传一个packet会放入一个应答队列等待应答。(为了加快传输效率,在DN1上收到客户端发来的数据后,数据流向两个地方:1. 将数据写入本地;2. 将数据传向下一台data node;)
(8)当一个Block传输完成之后,客户端再次请求NameNode上传第二个Block的服务器。(重复执行3-7步)。

6. 从HDFS读数据的流程

在这里插入图片描述
(1)客户端通过DistributedFileSystem向NameNode请求下载文件,NameNode通过查询元数据,找到文件块所在的DataNode地址。
(2)挑选一台DataNode(挑选原则:就近原则+节点的负载)服务器,请求读取数据。
(3)DataNode开始传输数据给客户端(从磁盘里面读取数据输入流,以Packet为单位来做校验),串行读取各节点数据。(相对来说也算是HDFS的一个缺点吧:被设计为一个不支持并行读写的文件系统)
(4)客户端以Packet为单位接收,先在本地缓存,然后写入目标文件。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值