HDFS(分布式文件存储系统)--执行流程及API操作
目录
流程
一、读取流程/下载
- 客户端发起RPC请求到NameNode
- NameNode在接收到请求之后会进行校验:
- 校验指定路径是否存在
- 校验文件是否有存在
- 如果文件存在,NameNode就会读取元数据,同时给客户端一个信号
- 客户端就会向NameNode要第一个Block的地址
- NameNode在收到请求之后会读取元数据,然后将第一个Block的地址放入队列中给客户端
- 默认一个Block是三个地址(3副本),客户端收到队列之后选择一个较近的节点来读取第一个Blcok,读取完成之后,会对这个Block进行checksum的验证;如果校验失败,客户端给NameNode一个信号,然后重新选取地址重新读取;如果校验成功,客户端就会向NameNode要第二个Block的地址,重复4,5,6三个过程。
- 客户端读取完所有的Block之后,会给NameNode一个结束信号,NameNode在收到信号之后会关闭文件
二、写入流程/上传
- 客户端发起RPC请求到NameNode
- NameNode在接收到请求之后会进行校验:
- 校验指定路径是否存在
- 校验写入路径是否有权限
- 校验指定路径中是否有同名文件
- 如果校验失败,则抛出异常,如果校验成功,记录元数据,NameNode会给客户端一个信号。
- 客户端在收到信号之后会向NameNode要第一个Block的存储位置。
- NameNode在收到请求之后,会等待DataNode的心跳,选取DataNode的地址放入队列中返回给客户端。默认情况下,NameNode会选择3个地址。
- 客户端收到队列中的3个地址,从这些地址中选择一个较近(网络拓扑距离)的节点写入第一个Block的第一个副本。
- 第一个副本所在的节点会通过pipeline(管道,实际就是NIO中的Channel)将第二个副本写到其他节点上,第二个副本所在节点在写到第三个副本。
- 写完之后,第三个副本所在的节点会给第二个副本所在的节点返回ack,第二个副本所在的节点收到ack之后会给第一个副本所在的节点返回ack,第一个副本所在的节点在给客户端返回ack
- 写完第一个Block之后,客户端会和NameNode要第二个Block的存储位置,然后重复5,6,7,8过程。
- 当写完所有的Block之后,客户端会给NameNode一个结束信号,NameNode就会关闭文件/关流,关流之后,这个文件就不可修改。
三、删除流程
-
客户端发起的RPC请求到NameNode。
-
NameNode在收到请求之后,会将这个请求记录到edits文件中,然后更新内存中的元数据,内存更新成功之后客户端会返回一个ack信号,此时这个文件对应的Block依然存储在DataNode。
-
在NameNode收到DataNode的心跳的时候,NameNode就会检查Block信息,会给DataNode进行心跳响应,要求删除对应的Block。DataNode在收到心跳响应之后才会真正删除Block。
API操作
吞吐量-----单位时间内节点或者集群读写的数据总量 1s读写100M --- 100M/s
高并发不一定是高吞吐的,但是高吞吐一般是高并发的
一、准备步骤
- 需要导入HDFS的依赖的jar包:
hadoop-2.7.1\share\hadoop\common\*.jar
hadoop-2.7.1\share\hadoop\common\lib\*.jar
hadoop-2.7.1\share\hadoop\hdfs\*.jar
hadoop-2.7.1\share\hadoop\hdfs\lib\*.jar
二、API操作
读取文件
@Test
public void testConnectNamenode() throws Exception{
Configuration conf=new Configuration();
FileSystem fs=FileSystem.get(new URI("hdfs://192.168.234.21:9000"), conf);
InputStream in=fs.open(new Path("/park/1.txt"));
OutputStream out=new FileOutputStream("1.txt");
IOUtils.copyBytes(in, out, conf);
}
上传文件
@Test
public void testPut() throws Exception{
Configuration conf=new Configuration();
conf.set("dfs.replication","1");
FileSystem fs=FileSystem.get(new URI("hdfs://192.168.234.21:9000"),conf,"root");
ByteArrayInputStream in=new ByteArrayInputStream("hello hdfs".getBytes());
OutputStream out=fs.create(new Path("/park/2.txt"));
IOUtils.copyBytes(in, out, conf);
}
删除文件
@Test
public void testDelete()throws Exception{
Configuration conf=new Configuration();
FileSystem fs=FileSystem.get(new URI("hdfs://192.168.234.21:9000"),conf,"root");
//true表示无论目录是否为空,都删除掉。可以删除指定的文件
fs.delete(new Path("/park01"),true);
//false表示只能删除不为空的目录。
fs.delete(new Path("/park01"),false);
fs.close();
}
在hdfs上创建文件夹
@Test
public void testMkdir()throws Exception{
Configuration conf=new Configuration();
FileSystem fs=FileSystem.get(new URI("hdfs://192.168.234.21:9000"),conf,"root");
fs.mkdirs(new Path("/park02"));
}
查询hdfs指定目录下的文件
@Test
public void testLs()throws Exception{
Configuration conf=new Configuration();
FileSystem fs=FileSystem.get(new URI("hdfs://192.168.234.21:9000"),conf,"root");
FileStatus[] ls=fs.listStatus(new Path("/"));
for(FileStatus status:ls){
System.out.println(status);
}
}
递归查看指定目录下的文件
@Test
public void testLs()throws Exception{
Configuration conf=new Configuration();
FileSystem fs=FileSystem.get(new URI("hdfs://192.168.234.214:9000"),conf,"root");
RemoteIterator<LocatedFileStatus> rt=fs.listFiles(new Path("/"), true);
while(rt.hasNext()){
System.out.println(rt.next());
}
}
重命名
@Test
public void testCreateNewFile() throws Exception{
Configuration conf=new Configuration();
FileSystem fs=FileSystem.get(new URI("hdfs://192.168.234.176:9000"),conf,"root");
fs.rename(new Path("/park"), new Path("/park01"));
}
获取文件的块信息
@Test
public void testCopyFromLoaclFileSystem() throws Exception{
Configuration conf=new Configuration();
FileSystem fs=FileSystem.get(new URI("hdfs://192.168.234.176:9000"),conf,"root");
BlockLocation[] data=fs.getFileBlockLocations(new Path("/park01/1.txt"),0,Integer.MaxValue);
for(BlockLocation bl:data){
System.out.println(bl);
}
}
还能用插件对hdfs来进行操作