1. 實驗環(huán)境
?操作系統(tǒng):Linux(Ubuntu18.04);
? Hadoop版本:3.1.3;
? JDK版本:1.8;
? Java IDE:IDEA;
? Hadoop偽分布式配置,。
2. HDFS
2.1 簡介
HDFS(Hadoop Distributed File System)分布式文件系統(tǒng),是Hadoop項目的核心子項目,是分布式計算中數(shù)據(jù)存儲管理的基礎,。
優(yōu)點:
- 兼容廉價的硬件設備
- 流數(shù)據(jù)讀寫
- 大數(shù)據(jù)集
- 簡單的文件模型
- 強大的跨平臺兼容性
局限性:
- 不適合低延遲數(shù)據(jù)訪問
- 無法高效存儲大量小文件
- 不支持多用戶寫入及任意修改文件
2.2 體系結構
HDFS采用分塊存儲策略,以塊作為存儲單位,一個文件被分成多個塊,默認一個塊64MB,。
作為一個分布式文件系統(tǒng),為了保證系統(tǒng)的容錯性和可用性,HDFS采用了多副本方式對數(shù)據(jù)進行冗余存儲,通常一個數(shù)據(jù)塊的多個副本會被分布到不同的數(shù)據(jù)節(jié)點上,數(shù)據(jù)塊1被分別存放到數(shù)據(jù)節(jié)點A和C上,數(shù)據(jù)塊2被存放在數(shù)據(jù)節(jié)點A和B上,。
HDFS采用抽象的塊概念可以帶來以下幾個好處:
● 支持大規(guī)模文件存儲:文件以塊為單位進行存儲,一個大規(guī)模文件可以被分拆成若干個文件塊,不同的文件塊可以被分發(fā)到不同的節(jié)點上,因此,一個文件的大小不會受到單個節(jié)點的存儲容量的限制,可以遠遠大于網(wǎng)絡中任意節(jié)點的存儲容量。
● 簡化系統(tǒng)設計:首先,大大簡化了存儲管理,因為文件塊大小是固定的,這樣就可以很容易計算出一個節(jié)點可以存儲多少文件塊;其次,方便了元數(shù)據(jù)的管理,元數(shù)據(jù)不需要和文件塊一起存儲,可以由其他系統(tǒng)負責管理元數(shù)據(jù),。
● 適合數(shù)據(jù)備份:每個文件塊都可以冗余存儲到多個節(jié)點上,大大提高了系統(tǒng)的容錯性和可用性,。
HDFS采用了主從(Master/Slave)
結構模型,在物理結構上是由計算機集群中的多個節(jié)點構成的這些節(jié)點分為兩類,一類叫“主節(jié)點”(Master Node)
或者也被稱為“名稱結點”(NameNode)
,另一類叫“從節(jié)點”(Slave Node)
或者也被稱為“數(shù)據(jù)節(jié)點”(DataNode)
。當后續(xù)開啟了start-dfs.sh
的時候可以jps
一下就知道了,。
2.2.1 NameNode
- 在HDFS中,名稱節(jié)點
(NameNode)
負責管理分布式文件系統(tǒng)的命名空間(Namespace)
,命名空間包含目錄、文件和塊,保存了兩個核心的數(shù)據(jù)結構,即FsImage
和EditLog
,。 FsImage
用于維護文件系統(tǒng)樹以及文件樹中所有的文件和文件夾的元數(shù)據(jù),。- 操作日志文件
EditLog
中記錄了所有針對文件的創(chuàng)建、刪除,、重命名等操作,。 - 名稱節(jié)點記錄了每個文件中各個塊所在的數(shù)據(jù)節(jié)點的位置信息,但并不持久化存儲這些信息,而是在系統(tǒng)每次啟動時掃描所有數(shù)據(jù)節(jié)點重構得到這些信息。
2.2.2 SecondaryNameNode
SecondaryNameNode
定期和NameNode
通信,請求其停止使用EditLog
文件,暫時將新的寫操作寫到一個新的文件edit.new上來,這個操作是瞬間完成,上層寫日志的函數(shù)完全感覺不到差別;SecondaryNameNode
通過HTTP GET
方式從NameNode
上獲取到FsImage
和EditLog
文件,并下載到本地的相應目錄下;SecondaryNameNode
將下載的FsImage
載到內存,一條一條地執(zhí)行EditLog
文件中的各項更新操作,使得內存中的FsImage
保持最新;這個過程就是EditLog
和FsImage
文件合并;- 執(zhí)行完(3)操作之后,通過post方式將新的
FsImage
文件發(fā)送到NameNode節(jié)點上,。 NameNode
將從SecondaryNameNode
接收到的FsImage
替換舊的FsImage文件,同時將edit.new替 換EditLog文件,通過這個過程EditLog就變小了,。
2.2.3 DataNode
- 數(shù)據(jù)節(jié)點是分布式文件系統(tǒng)
HDFS
的工作節(jié)點,負責數(shù)據(jù)的存儲和讀取,會根據(jù)客戶端或者是名稱節(jié)點的調度來進行數(shù)據(jù)的存儲和檢索,并且向名稱節(jié)點定期發(fā)送自己所存儲的塊的列表,。 - 每個數(shù)據(jù)節(jié)點中的數(shù)據(jù)會被保存在各自節(jié)點的本地
Linux
文件系統(tǒng)中。
2.2.4 通訊協(xié)議
? HDFS
是一個部署在集群上的分布式文件系統(tǒng),因此,很多數(shù)據(jù)需要通過網(wǎng)絡進行傳輸,。
? 所有的HDFS
通信協(xié)議都是構建在TCP/IP
協(xié)議基礎之上的,。
? 客戶端通過一個可配置的端口向名稱節(jié)點主動發(fā)起TCP連接,并使用ClientProtocol
與名稱節(jié)點進行交互。
? 名稱節(jié)點和數(shù)據(jù)節(jié)點之間則使用DatanodeProtocol
進行交互,。
? 一個遠程過程調用(RPC)
模型被抽象出來封裝ClientProtocol
和Datanodeprotocol
協(xié)議,。
? 客戶端與數(shù)據(jù)節(jié)點的交互是通過RPC(Remote Procedure Call)
來實現(xiàn)的。在設計上,名稱節(jié)點不會主動發(fā)起RPC
,而是響應來自客戶端和數(shù)據(jù)節(jié)點的RPC請求,。
在客戶端可以使用shell或是JavaApi進行HDFS的簡單操作,。
2.2.5 局限性
? 命名空間的限制:名稱節(jié)點是保存在內存中的,因此,名稱節(jié)點能夠容納的對象(文件、塊)的個數(shù)會受到內存空間大小的限制,。
? 性能的瓶頸:整個分布式文件系統(tǒng)的吞吐量,受限于單個名稱節(jié)點的吞吐量,。
? 隔離問題:由于集群中只有一個名稱節(jié)點,只有一個命名空間,因此,無法對不同應用程序進行隔離。
? 集群的可用性:一旦這個唯一的名稱節(jié)點發(fā)生故障,會導致整個集群變得不可用,。
3. 利用Shell命令HDFS進行交互
3.1 概要
- hadoop fs 適用于任何不同的文件系統(tǒng),比如本地文件系統(tǒng)和 HDFS 文件系統(tǒng):所有的的 FS shell 命令使用 URI 路徑作為參數(shù),。URI 格式是 scheme://authority/path。對 HDFS文件系統(tǒng),scheme 是 hdfs,對本地文件系統(tǒng),scheme 是 file,。
- hadoop dfs 只能適用于 HDFS 文件系統(tǒng)
- hdfs dfs 跟 hadoop dfs 的命令作用一樣,也只能適用于 HDFS 文件系統(tǒng)
3.2 目錄操作
創(chuàng)建HDFS的目錄
hdfs dfs -mkdir /目錄
注意要一層一層創(chuàng)建目錄,好像不能一蹴而就,。
列出HDFS的所有目錄
hdfs dfs -ls
刪除HDFS的目錄
hdfs dfs -rm -r /目錄
3.3 文件操作
文件上傳
hdfs dfs -put 源路徑 目的路徑
在主機里創(chuàng)建文件
然后上傳到HDFS中
文件內容查看
hdfs dfs -cat 文件路徑
文件下載
hdfs dfs -get 源文件地址 下載到的路徑
查看是否下載成功
文件的移動
hdfs dfs -mv 文件路徑 目的目錄
文件的復制
hdfs dfs -cp 文件路徑 目的目錄
文件的創(chuàng)建
hdfs dfs -touch 文件路徑
文件的追加
hdfs dfs -appendToFile 追加的內容文件路徑 被追加的文件路徑
在本地創(chuàng)建一個文件
對其進行添加操作
文件查看
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代碼實現(xiàn)功能:判斷指定文件在HDFS中是否存在,若不存在,則創(chuàng)建該文件,若存在,則打開文件進行內容追加;
在本地創(chuàng)建一個文件夾用于追加使用。
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];
/*
從輸出流中讀取一定數(shù)量的字節(jié),并將其存儲在緩沖區(qū)數(shù)組data中,。返回:
讀入緩沖區(qū)的總字節(jié)數(shù);
如果因為已經(jīng)到達流末尾而不再有數(shù)據(jù)可用,則返回 -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(); //主函數(shù)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寫數(shù)據(jù)(上傳數(shù)據(jù)的方式)時,
如果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是副本數(shù),n是要寫的DN數(shù),。在r>=3并且floor(r/2)>=n或者r>n(前提是文件是hflushed/appended)時添加新的DN。
*/
conf.set("fs.defaultFS", "hdfs://localhost:9000");
/*
默認文件系統(tǒng)的名稱,。URI形式,。uri's的scheme需要由(fs.SCHEME.impl)指定文件系統(tǒng)實現(xiàn)類。 uri's的authority部分用來指定host, port等,。默認是本地文件系統(tǒng),。
HA方式,這里設置服務名,例如:hdfs://111111
HDFS的客戶端訪問HDFS需要此參數(shù)。
偽分布是這樣的,如果是全分布的話,應該是你的映射名字不是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); // 文件不存在,創(chuàng)建文件
System.out.println("File Created Successfully!");
}
}catch(IOException e)
{
e.printStackTrace();
}
}
}
編譯器終端顯示沒有錯誤,。
然后在shell中cat是否添加
成功!追加成功!
第二題
編寫Java代碼實現(xiàn)功能:通過用戶實時輸入內容或本地文本內容完成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 { //一個死循環(huán)
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代碼實現(xiàn)功能:讀取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提交
注意最后的參數(shù)是main函數(shù)所在的類名
一:
二
三:
7. 結語
當初寫這幾題代碼的時候,在網(wǎng)上復制了不少代碼才寫出來的,。
我現(xiàn)在也不記得是復制哪位博主的。
如果侵權抄襲的話,麻煩聯(lián)系我吧!
最后
小生凡一,期待你的關注,。