HDFS的簡單基本操作
- 1. 實驗環境
- 2. HDFS
- 2.1 簡介
- 2.2 體系結構
- 2.2.1 NameNode
- 2.2.2 SecondaryNameNode
- 2.2.3 DataNode
- 2.2.4 通訊協議
- 2.2.5 局限性
- 3. 利用Shell命令HDFS進行互動
- 3.1 概要
- 3.2 目錄操作
- 3.3 檔案操作
- 4. 利用web界面管理HDFS
- 5. 使用JavaApi進行管理HDFS
- 5.1 導包并測驗
- 5.2 題目
- 第一題
- 第二題
- 第三題
- 6. 在集群上運行
- 7. 結語
- 最后
1. 實驗環境
?作業系統:Linux(Ubuntu18.04);
? Hadoop版本:3.1.3;
? JDK版本:1.8;
? Java IDE:IDEA;
? Hadoop偽分布式配置,
2. HDFS
2.1 簡介
HDFS(Hadoop Distributed File System)分布式檔案系統,是Hadoop專案的核心子專案,是分布式計算中資料存盤管理的基礎,
優點:
- 兼容廉價的硬體設備
- 流資料讀寫
- 大資料集
- 簡單的檔案模型
- 強大的跨平臺兼容性
局限性:
- 不適合低延遲資料訪問
- 無法高效存盤大量小檔案
- 不支持多用戶寫入及任意修改檔案
2.2 體系結構
HDFS采用分塊存盤策略,以塊作為存盤單位,一個檔案被分成多個塊,默認一個塊64MB,
作為一個分布式檔案系統,為了保證系統的容錯性和可用性,HDFS采用了多副本方式對資料進行冗余存盤,通常一個資料塊的多個副本會被分布到不同的資料節點上,資料塊1被分別存放到資料節點A和C上,資料塊2被存放在資料節點A和B上,

HDFS采用抽象的塊概念可以帶來以下幾個好處:
● 支持大規模檔案存盤:檔案以塊為單位進行存盤,一個大規模檔案可以被分拆成若干個檔案塊,不同的檔案塊可以被分發到不同的節點上,因此,一個檔案的大小不會受到單個節點的存盤容量的限制,可以遠遠大于網路中任意節點的存盤容量,
● 簡化系統設計:首先,大大簡化了存盤管理,因為檔案塊大小是固定的,這樣就可以很容易計算出一個節點可以存盤多少檔案塊;其次,方便了元資料的管理,元資料不需要和檔案塊一起存盤,可以由其他系統負責管理元資料,
● 適合資料備份:每個檔案塊都可以冗余存盤到多個節點上,大大提高了系統的容錯性和可用性,
HDFS采用了主從(Master/Slave)結構模型,在物理結構上是由計算機集群中的多個節點構成的這些節點分為兩類,一類叫“主節點”(Master Node)或者也被稱為“名稱結點”(NameNode),另一類叫“從節點”(Slave Node)或者也被稱為“資料節點”(DataNode),當后續開啟了start-dfs.sh的時候可以jps一下就知道了,


2.2.1 NameNode
- 在HDFS中,名稱節點
(NameNode)負責管理分布式檔案系統的命名空間(Namespace),命名空間包含目錄、檔案和塊,保存了兩個核心的資料結構,即FsImage和EditLog, FsImage用于維護檔案系統樹以及檔案樹中所有的檔案和檔案夾的元資料,- 操作日志檔案
EditLog中記錄了所有針對檔案的創建、洗掉、重命名等操作, - 名稱節點記錄了每個檔案中各個塊所在的資料節點的位置資訊,但并不持久化存盤這些資訊,而是在系統每次啟動時掃描所有資料節點重構得到這些資訊,

2.2.2 SecondaryNameNode
SecondaryNameNode定期和NameNode通信,請求其停止使用EditLog檔案,暫時將新的寫操作寫到一個新的檔案edit.new上來,這個操作是瞬間完成,上層寫日志的函式完全感覺不到差別;SecondaryNameNode通過HTTP GET方式從NameNode上獲取到FsImage和EditLog檔案,并下載到本地的相應目錄下;SecondaryNameNode將下載的FsImage載到記憶體,一條一條地執行EditLog檔案中的各項更新操作,使得記憶體中的FsImage保持最新;這個程序就是EditLog和FsImage檔案合并;- 執行完(3)操作之后,通過post方式將新的
FsImage檔案發送到NameNode節點上, NameNode將從SecondaryNameNode接收到的FsImage替換舊的FsImage檔案,同時將edit.new替 換EditLog檔案,通過這個程序EditLog就變小了,

2.2.3 DataNode
- 資料節點是分布式檔案系統
HDFS的作業節點,負責資料的存盤和讀取,會根據客戶端或者是名稱節點的調度來進行資料的存盤和檢索,并且向名稱節點定期發送自己所存盤的塊的串列, - 每個資料節點中的資料會被保存在各自節點的本地
Linux檔案系統中,

2.2.4 通訊協議
? HDFS是一個部署在集群上的分布式檔案系統,因此,很多資料需要通過網路進行傳輸,
? 所有的HDFS通信協議都是構建在TCP/IP協議基礎之上的,
? 客戶端通過一個可配置的埠向名稱節點主動發起TCP連接,并使用ClientProtocol與名稱節點進行互動,
? 名稱節點和資料節點之間則使用DatanodeProtocol進行互動,
? 一個遠程程序呼叫(RPC)模型被抽象出來封裝ClientProtocol和Datanodeprotocol協議,
? 客戶端與資料節點的互動是通過RPC(Remote Procedure Call)來實作的,在設計上,名稱節點不會主動發起RPC,而是回應來自客戶端和資料節點的RPC請求,
在客戶端可以使用shell或是JavaApi進行HDFS的簡單操作,
2.2.5 局限性
? 命名空間的限制:名稱節點是保存在記憶體中的,因此,名稱節點能夠容納的物件(檔案、塊)的個數會受到記憶體空間大小的限制,
? 性能的瓶頸:整個分布式檔案系統的吞吐量,受限于單個名稱節點的吞吐量,
? 隔離問題:由于集群中只有一個名稱節點,只有一個命名空間,因此,無法對不同應用程式進行隔離,
? 集群的可用性:一旦這個唯一的名稱節點發生故障,會導致整個集群變得不可用,
3. 利用Shell命令HDFS進行互動
3.1 概要
- hadoop fs 適用于任何不同的檔案系統,比如本地檔案系統和 HDFS 檔案系統:所有的的 FS shell 命令使用 URI 路徑作為引數,URI 格式是 scheme://authority/path,對 HDFS檔案系統,scheme 是 hdfs,對本地檔案系統,scheme 是 file,
- hadoop dfs 只能適用于 HDFS 檔案系統
- hdfs dfs 跟 hadoop dfs 的命令作用一樣,也只能適用于 HDFS 檔案系統
3.2 目錄操作
創建HDFS的目錄
hdfs dfs -mkdir /目錄

注意要一層一層創建目錄,好像不能一蹴而就,
列出HDFS的所有目錄
hdfs dfs -ls

洗掉HDFS的目錄
hdfs dfs -rm -r /目錄

3.3 檔案操作
檔案上傳
hdfs dfs -put 源路徑 目的路徑
在主機里創建檔案

然后上傳到HDFS中

檔案內容查看
hdfs dfs -cat 檔案路徑

檔案下載
hdfs dfs -get 源檔案地址 下載到的路徑

查看是否下載成功

檔案的移動
hdfs dfs -mv 檔案路徑 目的目錄

檔案的復制
hdfs dfs -cp 檔案路徑 目的目錄


檔案的創建
hdfs dfs -touch 檔案路徑

檔案的追加
hdfs dfs -appendToFile 追加的內容檔案路徑 被追加的檔案路徑
在本地創建一個檔案

對其進行添加操作

檔案查看
hdfs dfs -text 檔案路徑

檔案洗掉
hdfs dfs -rm 檔案路徑

4. 利用web界面管理HDFS
訪問url:http://localhost:9870
就可以看到管理頁面的!應該是可以的!我沒試過,
附上老師的截圖

5. 使用JavaApi進行管理HDFS
5.1 導包并測驗
Idea匯入hadoop jar包



找到檔案路徑安裝即可,

添加指定的jar檔案

看到這里的jar包即可成功了,

測驗代碼

可以運行就行了!
5.2 題目
第一題
撰寫Java代碼實作功能:判斷指定檔案在HDFS中是否存在,若不存在,則創建該檔案,若存在,則打開檔案進行內容追加;
在本地創建一個檔案夾用于追加使用,

import java.io.*;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
public class HDFSmkdir {
public static void appendToFile(Configuration conf, String localFilePath, String remoteFilePath) {
Path remotePath = new Path(remoteFilePath); //傳入hdfs檔案路徑
try(
FileSystem fs = FileSystem.get(conf);
FileInputStream in = new FileInputStream(localFilePath);){
//new一個檔案的輸入流的物件,并加入需要add的檔案路徑
FSDataOutputStream out = fs.append(remotePath); // 將hdfs中的檔案讀入
byte[] data = new byte[1024];
/*
從輸出流中讀取一定數量的位元組,并將其存盤在緩沖區陣列data中,回傳:
讀入緩沖區的總位元組數;
如果因為已經到達流末尾而不再有資料可用,則回傳 -1,
*/
int read = -1;
while((read = in.read(data)) > 0) {
out.write(data,0,read); // 添加hdfs檔案之中
}
out.close(); // 關閉hdfs的檔案
}catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
new Mkdir(); //主函式new一個Mkdir物件
}
}
class Mkdir{
public Mkdir(){
String filename = "/user/hadoop/input/hdfs-site.xml"; //需要添加本地內容的檔案
Configuration conf = new Configuration(); // new一個組態檔的物件
conf.set("dfs.client.block.write.replace-datanode-on-failure.enable", "true");
/*
在進行pipeline寫資料(上傳資料的方式)時,
如果DN或者磁盤故障,客戶端將嘗試移除失敗的DN,然后寫到剩下的磁盤,
一個結果是,pipeline中的DN減少了,這個特性是添加新的DN到pipeline,
這是一個站點范圍的選項,
*/
conf.set("dfs.client.block.write.replace-datanode-on-failure.policy","NEVER");
/*
此屬性僅在dfs.client.block.write.replace-datanode-on-failure.enable設定為true時有效,
ALWAYS: 總是添加新的DN
NEVER: 從不添加新的DN
DEFAULT: 設r是副本數,n是要寫的DN數,在r>=3并且floor(r/2)>=n或者r>n(前提是檔案是 hflushed/appended)時添加新的DN,
*/
conf.set("fs.defaultFS", "hdfs://localhost:9000");
/*
默認檔案系統的名稱,URI形式,uri's的scheme需要由(fs.SCHEME.impl)指定檔案系統實作類, uri's的authority部分用來指定host, port等,默認是本地檔案系統,
HA方式,這里設定服務名,例如:hdfs://111111
HDFS的客戶端訪問HDFS需要此引數,
偽分布是這樣的,如果是全分布的話,應該是你的映射名字不是localhost了,應該,
*/
conf.set("fs.hdfs.impl", "org.apache.hadoop.hdfs.DistributedFileSystem");
try {
FileSystem fs = FileSystem.get(conf);
Path path = new Path(filename);
if(fs.exists(path)) {
System.out.println("File \""+ filename +"\" Exist!");
// 檔案存在,追加內容
try {
String localFilePath = "/home/zqc/add.txt";
//需要追加的檔案內容的路徑
HDFSmkdir.appendToFile(conf, localFilePath, filename);
System.out.println("File Written Successfully!");
}catch(Exception e) {
e.printStackTrace();
}
}
else {
System.out.println("File \""+ filename +"\" Not Exist!");
fs.createNewFile(path); // 檔案不存在,創建檔案
System.out.println("File Created Successfully!");
}
}catch(IOException e)
{
e.printStackTrace();
}
}
}
編譯器終端顯示沒有錯誤,
然后在shell中cat是否添加

成功!追加成功!
第二題
撰寫Java代碼實作功能:通過用戶實時輸入內容或本地文本內容完成HDFS檔案寫入操作,用戶輸入“exit”結束寫入;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Scanner;
public class HDFSFileWritten {
public static void appendToFile(Configuration conf, String localFilePath, String remoteFilePath) {
Path remotePath = new Path(remoteFilePath);
try (FileSystem fs = FileSystem.get(conf);
FileInputStream in = new FileInputStream(localFilePath);) {
FSDataOutputStream out = fs.append(remotePath);
byte[] data = new byte[1024];
int read = -1;
while ((read = in.read(data)) > 0) {
out.write(data, 0, read);
}
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
new HDFSWrite();
}
}
class HDFSWrite {
public HDFSWrite() {
System.out.println("Please Choose The Way To Write");
System.out.println("1.Written On Real Time");
System.out.println("2.Written By Local File");
Scanner str = new Scanner(System.in);
String s = str.nextLine();
String filename = "/user/hadoop/input/hdfs-site.xml";
Path writtenpath = new Path(filename);
Configuration conf = new Configuration();
//配置資訊都和上面的一樣的!
conf.set("dfs.client.block.write.replace-datanode-on-failure.enable", "true");
conf.set("dfs.client.block.write.replace-datanode-on-failure.policy", "NEVER");
conf.set("fs.defaultFS", "hdfs://localhost:9000");
conf.set("fs.hdfs.impl", "org.apache.hadoop.hdfs.DistributedFileSystem");
if (s.equals("1")) { //如果選擇了1的話,就是自己輸入
System.out.println("Please Input The Content You Want To Append:");
Scanner scanner = new Scanner(System.in);
String string;
try { //一個死回圈
while (true) {
FileSystem fs = FileSystem.get(conf);
FSDataOutputStream fsdataoutput = fs.append(writtenpath);
string = scanner.nextLine();
if (string.equals("exit")) { //如果輸入是exit就退出
System.out.println("APPEND SUCCESSFULLY");
break;
}
try { // 不然的話就是輸入輸入檔案的內容
fsdataoutput.write(string.getBytes());
fsdataoutput.flush();
fsdataoutput.close();
} catch (Exception e) {
e.printStackTrace();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
if (s.equals("2")) { //如果是2的話就直接添加
try {
String localFilePath = "/home/zqc/add.txt";
HDFSFileWritten.appendToFile(conf, localFilePath, filename);
System.out.println("APPEND SUCCESSFULLY");
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
添加成功了,

本地檔案內容為JAVA HADOOP 添加成功,

第三題
撰寫Java代碼實作功能:讀取HDFS上的檔案的內容并輸出到控制臺
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils;
public class HDFSReadFile {
public static void main(String[] args) {
new ReadFile();
}
}
class ReadFile {
public ReadFile() {
try{
Configuration conf = new Configuration();
conf.set("fs.defaultFS", "hdfs://localhost:9000");
conf.set("fs.hdfs.impl", "org.apache.hadoop.hdfs.DistributedFileSystem");
FileSystem fs = FileSystem.get(conf);
FSDataInputStream file = fs.open(new Path("/user/hadoop/input/hdfs-site.xml"));
IOUtils.copyBytes(file, System.out, 1024);
fs.close();
}catch(Exception e) {
e.printStackTrace();
}
}
}

6. 在集群上運行
-
匯出為jar包

-
點擊這里,然后build

就可以看到生成了.jar包就可以了

在IDEA終端job提交
注意最后的引數是main函式所在的類名
一:

二

三:

7. 結語
當初寫這幾題代碼的時候,在網上復制了不少代碼才寫出來的,
我現在也不記得是復制哪位博主的,
如果侵權抄襲的話,麻煩聯系我吧!
最后
小生凡一,期待你的關注,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/287358.html
標籤:其他
下一篇:演算法和演算法分析
