一 簡(jiǎn)介
HDFS,它是一個(gè)文件系統(tǒng)茴丰,用于存儲(chǔ)文件困介,通過(guò)目錄樹來(lái)定位文件:其次,它是分布式的铃诬,由很多服務(wù)器聯(lián)合起來(lái)實(shí)現(xiàn)其功能祭陷,集群中的服務(wù)器有各自的角色。
HDFS的設(shè)計(jì)適合一次寫入趣席,多次讀出的場(chǎng)景兵志,且不支持文件的修改。適合用來(lái)做數(shù)據(jù)分析宣肚,并不適合用來(lái)做網(wǎng)盤應(yīng)用毒姨。
二 體系結(jié)構(gòu)和基本概念
1 特點(diǎn):
- 可拓展
- 容錯(cuò):有備份
- 高可用:對(duì)于多個(gè)Master
- 高吞吐:多臺(tái)機(jī)器并發(fā)讀
2 設(shè)計(jì)分布式文件系統(tǒng)思路
1 系統(tǒng)架構(gòu)
- 有多臺(tái)Masters,其中一臺(tái)為Active狀態(tài)钉寝,其他Standby
- 多個(gè)Slaves同時(shí)向Active的Master發(fā)送Heartbeat
- 若Active的Master出現(xiàn)宕機(jī)弧呐,則轉(zhuǎn)向其他Master
2 日志存取
- Client有200M的log文件需要存儲(chǔ)闸迷,首先與Master進(jìn)行RPC通通信
- Master將有剩余空間的Slave傳給Client
-
Client將log文件傳給該Slave
讀取 - 當(dāng)另一臺(tái)Clinet需要讀取時(shí),同理通過(guò)Master
但是以上存在問(wèn)題俘枫,沒有副本腥沽。當(dāng)該Slave宕機(jī),數(shù)據(jù)無(wú)法讀取鸠蚪。
因此存數(shù)據(jù)時(shí)今阳,需要同時(shí)復(fù)制給其他Slaves。(通過(guò)Buket復(fù)制茅信,而不是全部寫完再?gòu)?fù)制)
三 HDFS Shell
查看HDFS Shell命令
在hadoop文件夾下的bin中執(zhí)行
./hdfs dfs
命令
[-appendToFile <localsrc> ... <dst>] #追加內(nèi)容
[-cat [-ignoreCrc] <src> ...]#查看內(nèi)容
[-checksum <src> ...]#查看
[-chgrp [-R] GROUP PATH...]#改變所有組
[-chmod [-R] <MODE[,MODE]... | OCTALMODE> PATH...]#改變?nèi)?br> [-chown [-R] [OWNER][:[GROUP]] PATH...]#改變所屬用戶
[-copyFromLocal [-f] [-p] [-l] [-d] [-t <thread count>] <localsrc> ... <dst>]#從本地復(fù)制一份上傳到HDFS
[-copyToLocal [-f] [-p] [-ignoreCrc] [-crc] <src> ... <localdst>]#吧HDFS考到本地
[-count [-q] [-h] [-v] [-t [<storage type>]] [-u] [-x] [-e] <path> ...]#統(tǒng)計(jì)數(shù)量
[-cp [-f] [-p | -p[topax]] [-d] <src> ... <dst>]#拷貝
[-createSnapshot <snapshotDir> [<snapshotName>]]#創(chuàng)建快照
[-deleteSnapshot <snapshotDir> <snapshotName>]#刪除快照
[-df [-h] [<path> ...]]#查看磁盤使用情況
[-du [-s] [-h] [-v] [-x] <path> ...]
[-expunge]
[-find <path> ... <expression> ...]
[-get [-f] [-p] [-ignoreCrc] [-crc] <src> ... <localdst>] #下載
[-getfacl [-R] <path>]
[-getfattr [-R] {-n name | -d} [-e en] <path>]
[-getmerge [-nl] [-skip-empty-file] <src> <localdst>]
[-head <file>]
[-help [cmd ...]]
[-ls [-C] [-d] [-h] [-q] [-R] [-t] [-S] [-r] [-u] [-e] [<path> ...]]
[-mkdir [-p] <path> ...]
[-moveFromLocal <localsrc> ... <dst>]
[-moveToLocal <src> <localdst>]
[-mv <src> ... <dst>]
[-put [-f] [-p] [-l] [-d] <localsrc> ... <dst>] #上傳
[-renameSnapshot <snapshotDir> <oldName> <newName>]
[-rm [-f] [-r|-R] [-skipTrash] [-safely] <src> ...] #刪除
[-rmdir [--ignore-fail-on-non-empty] <dir> ...] #刪除文件夾
[-setfacl [-R] [{-b|-k} {-m|-x <acl_spec>} <path>]|[--set <acl_spec> <path>]]
[-setfattr {-n name [-v value] | -x name} <path>]
[-setrep [-R] [-w] <rep> <path> ... ]#修改副本數(shù)
[-stat [format] <path> ...] #查看文件信息
[-tail [-f] <file>] #查看末尾
[-test -[defsz] <path>]
[-text [-ignoreCrc] <src> ...] #查看內(nèi)容
[-touch [-a] [-m] [-t TIMESTAMP ] [-c] <path> ...]
[-touchz <path> ...]
[-truncate [-w] <length> <path> ...]
[-usage [cmd ...]]
將HDFS加入環(huán)境變量
為方便操作盾舌,可將hdfs添加到環(huán)境變量
- 進(jìn)入hadoop目錄
vi /etc/profile
- 添加hadoop目錄
export HADDOP_HOME=/bigdata/hadoop-3.2.0
- 添加path
export PATH=$PATH:$JAVA_HOME/bin:$HADOOP_HOME/bin
查看文件 -ls
查看HDFS下的文件
hdfs dfs -ls /
循環(huán)查看目錄
hdfs dfs -ls -R
自動(dòng)讀取core-site.xml的信息,得到namenode的位置蘸鲸,不需要指定
查看Linux下的文件
ls
將文件寫入HDFS -put
將文件上傳到根目錄
hdfs dfs -put 文件名 hdfs://主機(jī)名:端口號(hào)/
刪除文件
hdfs dfs -rm 文件名
文件通過(guò)切割上傳
BlockSize: 128M
該文件174.76M被切成兩塊
在該目錄下可看到文件大醒础(單位為字節(jié))
/bigdata/tmp/dfs/data/current/BP-595572733-127.0.0.1-1563891152282/current/finalized/subdir0/subdir0
紅框中為切塊的大小,相加即為文件大小
從HDFS上下載文件 -get
hdfs dfs -get /文件名 /保存路徑/保存文件名
下載多個(gè)文件并合并為一個(gè)文件
hdfs dfs -getmerge /a.txt /b.txt /home/c.txt
a.txt酌摇,b.txt為下載的文件膝舅,c.txt為存儲(chǔ)的文件名
查看文件 -cat 或 -text
hdfs dfs -cat /文件名(HDFS上的文件)
創(chuàng)建目錄 -mkdir
hdfs dfs -mkdir -p log/2019
從HDFS上刪除文件 -rm
hdfs dfs -rm /文件名
修改權(quán)限 -chmod
hdfs dfs -chmod +w /文件名
計(jì)數(shù) -count
hdfs dfs -count /
四 HDFS的Java編程API
將本地文件上傳至HDFS
1 創(chuàng)建maven項(xiàng)目
參考 https://blog.csdn.net/qq_35437792/article/details/80631434
2
package myhadoop.cn;
import com.google.common.annotations.VisibleForTesting;
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;
import org.junit.Before;
import org.junit.Test;
import java.io.*;
public class HDFSAPI {
private FileSystem fileSystem = null;
@Before
public void init() throws Exception {
//與HDFS建立連接,知道NameNode地址即可
Configuration conf = new Configuration();
System.setProperty("HADOOP_USER_NAME","root");
conf.set("fs.defaultFS", "hdfs://myhadoop.cn:9000");
fileSystem = FileSystem.get(conf);
}
@Test
public void testUpload() throws Exception {
//打開本地文件系統(tǒng)的一個(gè)文件作為輸入流
InputStream in = new FileInputStream("C://Users//33598//Desktop//salary.txt");
//使用hdfs的filesystem打開一個(gè)輸入流
OutputStream out = fileSystem.create(new Path("/salary.txt"));
IOUtils.copyBytes(in, out, 1024, true);
}
}
踩雷1
參考 https://blog.csdn.net/fragrant_no1/article/details/85842038
踩雷2 could only be written to 0 of the 1 minReplication nodes.
(忘記截圖)
踩雷3 Permission denied: user=hadoop, access=WRITE, inode="/":root:supergroup:drwxr-xr-x
增加環(huán)境變量
并且重啟IDEA
成功窑多!
創(chuàng)建目錄
@Test
public void testMkdir() throws Exception {
fileSystem.mkdirs(new Path("/a/b"));
fileSystem.close();
}
刪除文件
@Test
public void testDel() throws Exception {
boolean flag = fileSystem.delete(new Path("/a"),false);
System.out.println(flag);
fileSystem.close();
}
delete()函數(shù)的第二個(gè)參數(shù)false為不是遞歸刪除仍稀,true為遞歸刪除。
以上代碼刪除剛才創(chuàng)建的a文件夾埂息。由于a文件夾下還存在b文件夾技潘,當(dāng)參數(shù)為false時(shí)報(bào)錯(cuò):
org.apache.hadoop.ipc.RemoteException(org.apache.hadoop.fs.PathIsNotEmptyDirectoryException): `/a is non empty': Directory is not empty
改為true:
成功!
五 HDFS架構(gòu)原理
概念
- 文件切塊:打開一個(gè)文件進(jìn)行寫入(按字節(jié))千康,當(dāng)?shù)竭_(dá)切塊大小后享幽,將該文件關(guān)閉,重新打開另一個(gè)文件吧秕,繼續(xù)寫入。
- 副本的存放:當(dāng)一臺(tái)機(jī)器宕機(jī)后用戶仍可從其他機(jī)器讀取數(shù)據(jù)
- 元數(shù)據(jù):上傳的數(shù)據(jù)迹炼,datanode存放真正的數(shù)據(jù)砸彬,namenode存放數(shù)據(jù)的描述信息(元數(shù)據(jù))
重要組件
- NameNode:負(fù)責(zé)管理
- DataNode:存儲(chǔ)數(shù)據(jù),維護(hù)block
-
SecondaryNameNode (偽分布式和非高可用的集群才有):幫助NameNode進(jìn)行數(shù)據(jù)的同步(對(duì)磁盤和內(nèi)存進(jìn)行同步)
HDFS aRchitecture
一個(gè)Rack1上放多臺(tái)服務(wù)器斯入,block存放在服務(wù)器上(綠色方塊)砂碉。Client上運(yùn)行程序,讀取數(shù)據(jù)刻两。首先向Namenode讀取數(shù)據(jù)信息(文件存儲(chǔ)份數(shù)增蹭,存儲(chǔ)機(jī)器),反回元數(shù)據(jù)磅摹,Client根據(jù)返回的元數(shù)據(jù)滋迈,從服務(wù)器上讀取霎奢。
Namenode
寫數(shù)據(jù): - 將文件切分成blk_1
- 將數(shù)據(jù)包blk_1寫到h0上,再進(jìn)行水平復(fù)制到h1 h3上
- 將剩下的數(shù)據(jù)blk_2寫到h0上饼灿,復(fù)制到h2 h4上
Namenode工作機(jī)制
- 目錄樹:NameNode記錄文件夾的邏輯結(jié)構(gòu)(并非真正的文件夾)
- fsimage: 磁盤中的數(shù)據(jù)(某一段時(shí)間的信息)幕侠,最新信息在內(nèi)存中
- edits: 標(biāo)記是否上傳成功,元數(shù)據(jù)每隔一段時(shí)間清理標(biāo)記失敗的文件
- fstime: 新版本不保存在這里碍彭,保存在Linux文件系統(tǒng)中
SecondaryNameNode工作機(jī)制
HA(高可用)
在新版中并不替換晤硕,而是生成新的名字保存。
DataNode工作機(jī)制
六 Hadoop的RPC通信機(jī)制
什么是RPC(Remote Procedure Call):(https://baike.baidu.com/item/%E8%BF%9C%E7%A8%8B%E8%BF%87%E7%A8%8B%E8%B0%83%E7%94%A8/7854346)庇忌,它是一種通過(guò)網(wǎng)絡(luò)從遠(yuǎn)程計(jì)算機(jī)程序上請(qǐng)求服務(wù)舞箍,而不需要了解底層網(wǎng)絡(luò)技術(shù)的協(xié)議。RPC協(xié)議假定某些傳輸協(xié)議的存在皆疹,如TCP或UDP疏橄,為通信程序之間攜帶信息數(shù)據(jù)。在OSI網(wǎng)絡(luò)通信模型中墙基,RPC跨越了傳輸層和應(yīng)用層软族。RPC使得開發(fā)包括網(wǎng)絡(luò)分布式多程序在內(nèi)的應(yīng)用程序更加容易。
例子
服務(wù)端
package myhadoop.rpc;
import org.apache.hadoop.HadoopIllegalArgumentException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.ipc.RPC;
import java.io.IOException;
public class RPCServer implements RPCService{
public static void main(String[] args) throws HadoopIllegalArgumentException, IOException {
Configuration conf = new Configuration();
RPC.Server server = new RPC.Builder(conf)
.setProtocol(RPCService.class)
.setBindAddress("192.168.106.1")
.setPort(9527)
.setInstance(new RPCServer())
.build();
server.start();
}
public String sayHi(String name) {
return "Hi~" + name;
}
}
服務(wù)端接口
package myhadoop.rpc;
public interface RPCService {
//客戶端指定的versionID,使客戶端服務(wù)端一致
public static final long versionID = 10010L;
public String sayHi(String name);
}
客戶端
package myhadoop.rpc;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.ipc.RPC;
import java.io.IOException;
import java.net.InetSocketAddress;
public class RPCClient {
public static void main(String[] args) throws IOException {
Configuration conf = new Configuration();
//在客戶端獲取代理對(duì)象残制,有代理對(duì)象就可以獲取目標(biāo)對(duì)象(RPCServer)的方法
RPCService proxy = RPC.getProxy(RPCService.class,10010, new InetSocketAddress("192.168.106.1", 9527),conf);
//
String result = proxy.sayHi("Tom");
System.out.println(result);
RPC.stopProxy(proxy);
}
}
客戶端調(diào)用服務(wù)器段方法立砸,最終將值返回給客戶端