Hadoop 分布式存储(hdfs)系统介绍
- hadoop组成
- 分布式存储(hdfs)
- hadoop1.x 存储系统
- hadoop 2.x 存储系统
- 存储账本原理
- JournalNodes
- 常用命令操作
- Java 代码实现
- 配置文件
- 获取对象
- 创建文件夹
- 上传文件
- 下载文件
- 递归列出文件夹中所有内容(包含子目录)
- 参考文献
hadoop组成
hadoop 集群主要做了两件事: 分布式存储(hdfs) 和分布式计算(map-reduce)。本文主要对hadoop 分布式存储理解做一个记录 帮助以后快速记忆 。
分布式存储(hdfs)
分为 hadoop 1.x 和 hadoop 2.x
hadoop1.x 存储系统
NameNode :
1,负责接收用户的请求
2,维护一套账本 记录所有的文件存储位置。
存储系统的本质操作:
在机房中就是这样:
Switch是交换机(路由器)、Rack代表机房里的一个机柜,每个机柜里都有很多台服务器,称之为节点。
hadoop 2.x 存储系统
1,1.x 有一个问题就是 NameNode 不能扩展 万一宕机 账本没有了 整个系统就崩溃了 2.x 可以配置多个NameNode .
2, 单个NameNode 虽然账本很小 但是内存也是有限的。
存储账本原理
账本组成有: fsimage , edits
fsimage 是整个系统的账本信息
edits 你运行的时候 所做的操作记录
1,hdfs 启动的时候 首先加载 fsimage 进入内存
2,客户端对hdfs 的操作信息会写入 edits 。 当系统崩溃的时候 下一次启动 hdfs 先恢复edits 里面的操作 。如果正常结束 hdfs 这里面的所有记录 应该为空。
3,随着edits内容增大,就需要在一定时间点和fsimage合并。
左边是NameNode 右边方块是 SecondNameNode .
SecondNameNode 是可以配置的 主要任务是帮助NameNode 上的账本完成不定时的合并 。
secondarynamenode在合并edits和fsimage时需要消耗的内存和namenode差不多,所以一般把namenode和secondarynamenode放在不同的机器上。
fs.checkpoint.period: 默认是一个小时(3600s)
fs.checkpoint.size: edits达到一定大小时也会触发合并(默认64MB)
JournalNodes
两个NameNode为了数据同步,会通过一组称作JournalNodes的独立进程进行相互通信。当active状态的NameNode的命名空间有任何修改时,会告知大部分的JournalNodes进程。standby状态的NameNode有能力读取JNs中的变更信息,并且一直监控edit log的变化,把变化应用于自己的命名空间。standby可以确保在集群出错时,命名空间状态已经完全同步了
常用命令操作
本节将讲解HDFS中,常见的命令行操作。HDFS与我们传统的文件系统类似,都可以存储文件,查看文件的内容,删除文件,并且文件也有着权限的概念。事实上,HDFS也借鉴了Linux文件系统的目录树结构和权限系统。
一、基本操作
启动:
停止:
1、创建目录
hadoop fs -mkdir /hdfs #在根目录下创建hdfs文件夹
2、查看目录
hadoop fs -ls / #列出跟目录下的文件列表
drwxr-xr-x - root supergroup 0 2016-03-05 00:06 /hdfs
3、级联创建目录
hadoop fs -mkdir -p /hdfs/d1/d2
4、级联列出目录
hadoop fs -ls -R /
drwxr-xr-x - root supergroup 0 2016-03-05 00:10 /hdfs
drwxr-xr-x - root supergroup 0 2016-03-05 00:10 /hdfs/d1
drwxr-xr-x - root supergroup 0 2016-03-05 00:10 /hdfs/d1/d2
5、上传本地文件到HDFS
echo “hello hdfs” >>local.txt
hadoop fs -put local.txt /hdfs/d1/d2
6、查看HDFS中文件的内容
hadoop fs -cat /hdfs/d1/d2/local.txt
hello hdfs
7、下载hdfs上文件的内容
hadoop fs -get /hdfs/d1/d2/local.txt
8、删除hdfs文件hadoop fs -rm /hdfs/d1/d2/local.txt
Deleted /hdfs/d1/d2/local.txt
9、删除hdfs中目录
hadoop fs -rmdir /hdfs/d1/d2
10、修改文件的权限
hadoop fs -ls /hdfs
drwxr-xr-x - root supergroup 0 2016-03-05 00:21 /hdfs/d1 #注意文件的权限
hadoop fs -chmod 777 /hdfs/d1
drwxrwxrwx - root supergroup 0 2016-03-05 00:21 /hdfs/d1 #修改后
11、修改文件所属的用户
hadoop fs -chown admin /hdfs/d1 #修改文件所属用户为admin
hadoop fs -ls /hdfs
drwxrwxrwx - admin supergroup 0 2016-03-05 00:21 /hdfs/d1
12、修改文件的用户组
hadoop fs -chgrp admin /hdfs/d1
hadoop fs -ls /hdfs
drwxrwxrwx - admin admin 0 2016-03-05 00:21 /hdfs/d1
Java 代码实现
配置文件
<!--hadoopHDFS依赖-->
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<version>2.6.0-cdh5.4.7</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-hdfs</artifactId>
<version>2.6.0-cdh5.4.7</version>
</dependency>
<!--hadoop使用到了jdk自带的tools.jar-->
<dependency>
<groupId>jdk.tools</groupId>
<artifactId>jdk.tools</artifactId>
<version>1.7</version>
<scope>system</scope>
<systemPath>${JAVA_HOME}/lib/tools.jar</systemPath>
</dependency>
<repositories>
<repository>
<id>cloudera</id>
<url>https://repository.cloudera.com/artifactory/cloudera-repos/</url>
</repository>
</repositories>
获取对象
Hadoop提供FileSystem类用于表示HDFS文件系统,我们对HDFS所有的操作都是要基于这个类来进行完成。因此我们首先要做的就是获取这个类的实例。
public class HDFSAPI {
public static String nameNodeUrl="hdfs://115.28.65.149:9000";
public static Configuration configuration=new Configuration();
public static FileSystem fileSystem;
@BeforeClass
public static void beforeClass() throws Exception{
Configuration configuration=new Configuration();
configuration.set("fs.defaultFS", "hdfs://115.28.65.149:9000");
URI uri = new URI(nameNodeUrl);
String user="root";
fileSystem=FileSystem.get(uri,configuration,user);
System.out.println(fileSystem);
}
...
}
运行程序输出:
DFS[DFSClient[clientName=DFSClient_NONMAPREDUCE_-1459561864_1, ugi=root (auth:SIMPLE)]]
说明:
因为通过网络来进行访问,所以我们必须配置HDFS的网络路径,实际上就是我们hadoop集群中core-site.xml配置的项的值。我们通过Configuration类来进行设置。只不过配置项的名字改为"fs.defaultFS"。这里我们是通过字符串来指定配置项的名字,实际上,Hadoop还提供了2个类CommonConfigurationKeysPublicCommonConf和CommonConfigurationKeys两个类,这两个类中包含了Hadoop客户端所有配置项的key和默认值,例如我们这里的"fs.defaultFS"就可以用CommonConfigurationKeysPublic.FS_DEFAULT_NAME_KEY 来指定。
我们使用FileSystem.get(uri,configuration,user);这个方法来获取FileSystem的实例,这里指定的user为root,实际上也可以不指定。主要是因为我在Linux系统上是通过root用户来启动hadoop集群的。这里显示指定root,那么我就有权限访问HDFS文件系统中的所有内容。如果不指定,就会以启动我们这个客户端的程序的身份去访问HDFS,由于笔者是在Windows操作系统上写的代码,用户名为Administer,那么可能就会有一些文件没有权限访问
创建文件夹
@Test
public void mkdirs() throws IOException{
Path path=new Path("/hdfs/javaapi");
final boolean exists = fileSystem.exists(path);
System.out.println(exists);//创建前
if(!exists){
fileSystem.mkdirs(path);
}
System.out.println(fileSystem.exists(path));//创建后
}
运行程序,控制台输出
false
true
说明:
Hadoop使用PATH对象表示HDFS文件中的路径,因为是仿照Linux系统的路径命名习惯,所以我们传入的路径是"/hdfs/javaapi"。在创建文件夹文件夹之前,我们依然要判断是否存在,不存在才可以创建,否则会抛出异常。
上传文件
@Test
public void uploadFile() throws Exception{
String uploadPath="/hdfs/javaapi/upload.txt";//文件保存路径
String content="test upload file";//文件内容
boolean overwrite=true;//如果文件存在就覆盖
final Path hdfsPath = new Path(uploadPath);
FSDataOutputStream out=fileSystem.create(hdfsPath, overwrite);
boolean close=true;//上传成功关闭输入流和输出流
int bufferSize=1024;//缓冲大小
ByteArrayInputStream in = new ByteArrayInputStream(content.getBytes());//输入流
IOUtils.copyBytes(in, out, bufferSize,close);
System.out.println(fileSystem.exists(hdfsPath));//判断文件是否上传到HDFS上
}
运行程序,控制台输出:
true
说明:
HDFS是提供了FSDataOutputStream对象来表示输出流,对应的有FSDataInputStream表示输入流。因为HSFS是需要通过流式的方式进行操作,所以我们把上传的内容content转换成了输入流。IOUtils是hadoop提供的一个工具类,帮我们从输入流往输出流中写数据。
下载文件
@Test
public void downloadFile() throws Exception {
String downloadPath = "/hdfs/javaapi/upload.txt";
FSDataInputStream inputStream = fileSystem.open(new Path(downloadPath));
IOUtils.copyBytes(inputStream, System.out, 1024, true);
}
运行程序,控制台输出
test upload file
说明:
FileSystem的open方法返回的是一个FSDataInputStream对象,表示指定文件的输入流。此处我们依然使用了工具类IOUtils,将获取到的内容显示在了控制台上(通过指定输出流为System.out).
递归列出文件夹中所有内容(包含子目录)
@Test
public void recursive() throws IOException {
FileStatus[] listStatus = fileSystem.listStatus(new Path("/hdfs"));
for (FileStatus fileStatus : listStatus) {
listFiles(fileStatus);
}
}
private void listFiles(FileStatus fileStatus) throws IOException {
Path path = fileStatus.getPath();
System.out.println(path);
if (!fileStatus.isDirectory()) {// 如果不是目录
FsPermission permission = fileStatus.getPermission();
String owner = fileStatus.getOwner();
short replication = fileStatus.getReplication();
long len = fileStatus.getLen();
System
.out.println("path:"+path+",permission:"+permission+",replication:"
+replication+",owenr:"+owner+",size:"+len);
} else {// 如果是目录
FileStatus[] listStatus = fileSystem.listStatus(path);
for (FileStatus childStatus : listStatus) {
listFiles(childStatus);
}
}
}
运行程序,控制台输出:
hdfs://115.28.65.149:9000/hdfs/d1
hdfs://115.28.65.149:9000/hdfs/javaapi
hdfs://115.28.65.149:9000/hdfs/javaapi/upload.txt
path:hdfs://115.28.65.149:9000/hdfs/javaapi/upload.txt,permission:rw-r–r--,replication:3,owenr:root,size:16
说明:
FileStatus对象用于表示HDFS中文件的元数据(包含文件的路径,文件大小、权限、备份数等信息)。
fileSystem.listStatus(new Path("/hdfs"))方法的返回值是FileStatus[]数组,表示的是/hdfs目录下所有的文件/文件夹的源信息。
通过fileStatus.getPath()f方法,我们可以获得一个文件的访问路径。通过fileStatus.isDirectory(),我们可以判断当前路径是文件还是文件夹。
FsPermission对象表示的是一个文件的权限信息,通过fileStatus.getPermission()方法获得。
文件的备份数,通过fileStatus.getReplication()获得,返回值是一个short整数。
文件的所有者通过fileStatus.getOwner()f方法获得。
参考文献
http://www.tianshouzhi.com/api/tutorials/hadoop/132