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來進行操作