HDFS體系結構
★ 分佈式文件系統
一種可以管理分佈在不同機器上的文件的操作系統。因爲,單一的一臺機器上的存儲已經不能滿足需要。不同主機上的文件可以通過網絡進行分享。也叫網絡操作系統,即NFS。通過網絡訪問的文件,對用戶和程序來說,如同本地一樣。其中HDFS就是其中一種分佈式操作系統。適合一次寫入,多次讀寫的情況。
★ HDFS 常用shell操作
在Hadoop中通過shell命令訪問HDFS文件系統中文件的命令方法是hadoop fs 開頭或hadoop dfs。
完整的命令應該格式是: hadoop fs schema://authority/path
完整的命令示例如:hadoop fs hdfs://hadooptest:9000/
hdfs://hadooptest:9000是conf/core-site.xml配置文件中fs.default.name對應的值。
下面介紹詳細命令:(下面的命令寫的是簡寫)
命令一:hadoop fs -help
[root@hadooptest ~]# hadoop fs -help
Warning: $HADOOP_HOME is deprecated.
hadoop fs is the command to execute fscommands. The full syntax is:
hadoop fs [-fs <local | file systemURI>] [-conf <configuration file>]
[-D <property=value>] [-ls <path>] [-lsr <path>] [-du<path>]
[-dus <path>] [-mv <src> <dst>] [-cp <src><dst>] [-rm [-skipTrash] <src>]
[-rmr [-skipTrash] <src>] [-put <localsrc> ... <dst>][-copyFromLocal <localsrc> ... <dst>]
[-moveFromLocal <localsrc> ... <dst>] [-get [-ignoreCrc][-crc] <src> <localdst>
[-getmerge <src> <localdst> [addnl]] [-cat <src>]
[-copyToLocal [-ignoreCrc] [-crc] <src> <localdst>][-moveToLocal <src> <localdst>]
[-mkdir <path>] [-report] [-setrep [-R] [-w] <rep> <path/file>]
[-touchz <path>] [-test -[ezd] <path>] [-stat [format]<path>]
[-tail [-f] <path>] [-text <path>]
[-chmod [-R] <MODE[,MODE]... | OCTALMODE> PATH...]
[-chown [-R] [OWNER][:[GROUP]] PATH...]
[-chgrp [-R] GROUP PATH...]
[-count[-q] <path>]
[-help [cmd]]
-fs [local | <file system URI>]: Specify the file system to use.
...........................
ps : 用於顯示命令的幫助文檔
命令二:hadoop fs -ls(r) <path> //顯示當前目錄下所有文件
顯示列表中,列表的顯示,類似Linux下ls -l 列出來的類似
d表示是文件夾 -表示是文件
再接着的9位表示的是權限,權限分組類似linux
再接着的是表示的是所屬的用戶和組
再接着的是文件的大小
再接着的是日期
命令三:hadoop fs -du(s) <path> //顯示目錄中所有文件大小
[root@hadooptest ~]# hadoop fs -dus /txt
Warning: $HADOOP_HOME is deprecated.
hdfs://hadooptest:9000/txt 36
[root@hadooptest ~]# hadoop fs -du /txt
Warning: $HADOOP_HOME is deprecated.
Found 2 items
15 hdfs://hadooptest:9000/txt/wealon.txt
21 hdfs://hadooptest:9000/txt/wealonxt.txt
命令四:hadoop fs -count[-q] <path> //顯示目錄中文件數量
[root@hadooptest ~]# hadoop fs -count /txt
Warning: $HADOOP_HOME is deprecated.
1 2 36hdfs://hadooptest:9000/txt
分別顯示了目錄數,文件數,文件大小及所計算的路徑
命令五:hadoop fs -mv <src> <dst> //移動多個文件到目標目錄
移動文件從HDFS的src到dst路徑
命令六:hadoop fs -cp <src> <dst> //複製多個文件到目標目錄
移動文件從HDFS的src到dst路徑
命令七:hadoop fs -rm(r) //刪除文件(夾)
要刪除文件夾,必須用rmr參數
命令八:hadoop fs -put <localsrc> <dst> //本地文件複製到hdfs
ps:localsrc表示的是本地路徑
命令九:hadoop fs -copyFromLocal //同put
命令十:hadoop fs -moveFromLocal //從本地文件移動到hdfs
命令十一:hadoop fs -get [-ignoreCrc] <src> <localdst> //複製文件到本地,可以忽略crc校驗
命令十二:hadoop fs -getmerge <src> <localdst> //將源目錄中的所有文件排序合併到一個文件中
PS:用於合併文件,把零散的小文件合併成一個大文件
命令十三:hadoop fs -cat <src> //在終端顯示文件內容
PS:查看
命令十四:hadoop fs -text <src> //在終端顯示文件內容
同-cat
命令十五:hadoop fs -copyToLocal [-ignoreCrc] <src> <localdst> //複製到本地
PS:From 與 To
命令十六:hadoop fs -moveToLocal <src> <localdst>
PS:From 與 To
命令十七:hadoop fs -mkdir <path> //創建文件夾
命令十八:hadoop fs -touchz <path> //創建一個空文件
PS:HDFS的命令,與linux的命令可以關聯起來,比較類似。
▲ 文件的副本數
對於在conf/hdfs-site.xml中配置的dfs.replication參數的值,表示的是HDFS中某個文件或block的副本數。默認是3
如之前設置的副本數是2,則上傳的所有文件的副本數都將是2個
如果在上傳了一部分文件後,更改了dfs.replication的值爲3
則,在修改之後上傳的文件或block的副本數爲3,而修改之前上傳的文件的副本數仍舊爲2.
如果要使修改之前上傳的文件的副本數從2變爲3,只需要執行如下的命令。
hadoop fs -setrep -R 3 /
命令:hadoop fsck -locations 用來查看副本情況
在一個dataNode中只保存一個副本,所以,如果一個集羣有3臺機器,而設置的副本數爲4,則,該參數的設置是不會生效的。
★ HDFS體系結構
兩個核心:NameNode 和 DataNode
▲ NameNode功能:
接收用戶的操作請求
是整個文件系統的管理節點
維護整個文件系統的文件目錄樹
維護文件或目錄的元信息及每個文件對應的數據塊列表
那麼它是怎麼實現如上功能的呢?
由以下fsimage、edits、fstime這三個文件來實現。
以上三個文件的位置是在core-site.xml文件中的屬性爲hadoop.tmp.dir的值來指定的,如/usr/local/hadoop1/tmp
在上述目錄下的dfs/name/current目錄下有如下四個文件:
edits fsimage fstime VERSION
其中:fsimage是元數據鏡像文件,存儲的是某一時間段NameNode內存的元數據信息
edits保存的是操作日誌文件
fstime 保存的是最近一次checkpoint的時間
那麼VERSION呢?
▲ SecondaryNameNode的功能
用來合併fsimage和edits成新的fsimage,合併後edits清空。
edits的合併有一定的條件:
條件一,達到一定的週期,默認是3600秒,即1小時。
條件二,達到一定的大小,默認是67108864,也即64M,如果大小已經達到了這個大小,則觸發合併的操作,而不管週期是否已經到3600秒。(見源文件)
默認SecondaryNameNode是跟NameNode安裝在同一個節點上的,這樣是不好的,因爲SecondaryNameNode和NameNode都是單節點的,所以,如果出現節點故障,SecondaryNameNode和NameNode都會受到影響。所以,最好是把Name和SecondaryNameNode在集羣環境下分別安裝在不同的節點中。
▲ DataNode的功能
它提供的是真實文件數據的存儲服務。
文件塊:它是文件的最基本的存儲單位。把一個大的文件劃分成小的block,默認的Block大小是64M。
在HDFS文件系統中,如果一個文件的大小小於一個Block塊的大小,則該文件並不佔用整個數據塊的存儲空間。
在linux文件系統中,如果一個文件的大小小於一個Block塊的大小,則該文件佔用的是該塊Block的大小。
▲ HDFS的權限
文件的權限都設計成與其它常用平臺類似,像Linux。當前,安全性僅限於簡單權限。誰啓動的NameNode,誰就被當成HDFS的超級用戶。
★ 用JAVA操作HDFS
用javaAPI操作HDFS,用到的Hadoop核心類有:
FileSystem 表示的是文件系統,對HDFS的操作的方法均來自於該類,這是一個抽象類。
Configuration 表示配置對象
URL 表示 表示路徑,在用URL的時候,路徑一定要加上schema,不支持簡寫
FileStatus 文件狀態,從該對象中可以獲取到文件的名稱、文件的權限、等等信息
Path 表示路徑,可以用它來描述一個文件或文件夾
FSDataInputStream 在用FileSystem讀取文件的時候,是把文件讀取到該流中,從而根據流的對接,可以把文件輸出到輸出流中。
IOUtils 一個IO工具類,用於處理IO操作,簡化了IO操作。
以下是測試的例子,不全,基本操作全在了。
▲ 初始化變量
private static String url = null;
private static Configuration conf = null;
private static FileSystem fs = null;
// 初始化url Configuration FileSystem
static {
try {
url = "hdfs://192.168.75.245:9000";
conf = new Configuration();
fs = FileSystem.get(URI.create(url), conf, "root");
}catch(Exception e) {
e.printStackTrace();
}
}
▲ 例子一:檢測HDFS中某個文件是否存在
/** 測試文件夾是否存在 **/
@Test
public void testFolderExist() throws Exception {
//路徑相當於:hdfs://hadooptest:9000/wealon
boolean exist = this.isFolderExist("/wealon");
System.out.println(exist);
}
/**
* 檢查某個文件夾是否存在
*
* @throws Exception
*/
private boolean isFolderExist(String path) throws Exception {
return fs.exists(new Path(path));
}
▲ 例子二:測試創建文件夾
/** 測試創建文件夾 **/
@Test
public void testCreateFolder() throws Exception {
// 創建文件夾
this.createFolder("/path");
}
/**
* 根據傳入的字符串創建文件夾
*
* @param folderName
*/
private void createFolder(String folderName) throws Exception {
fs.mkdirs(new Path(folderName));
}
▲ 例子三:創建文件
/** 測試創建文件 **/
@Test
public void testCreateFile() throws Exception {
// 創建空文件
this.createFile("/path/test.txt");
}
/**
* 根據字符串創建文件
*
* @param fileName
*/
private void createFile(String fileName) throws Exception {
fs.create(new Path(fileName));
}
▲ 例子四:測試讀取文件二
/** 測試讀取文件二 **/
@Test
public void tesReadFile2() throws Exception {
// 讀取文件 方法二
this.readFile2("HDFS://hadooptest:9000/core-site.xml");
}
/**
* 讀取文件方法二
*
* @param fileName
*/
private void readFile2(StringpathName) throwsException {
//設置讓程序識別HDFS協議
URL.setURLStreamHandlerFactory(newFsUrlStreamHandlerFactory());
URLurl = newURL(pathName);
InputStreamis = url.openStream();
IOUtils.copyBytes(is,System.out,1024, true);
}
▲ 例子五:測試讀取文件一
/** 測試讀取文件 **/
@Test
public void tesReadFile() throws Exception {
// 讀取文件方法一
this.readFile("/core-site.xml");
}
/**
* 讀取文件方法一
*
* @param fileName
*/
private void readFile(String pathName) throws Exception {
if (this.isFolderExist(pathName)){
FSDataInputStreamis = fs.open(new Path(pathName));
IOUtils.copyBytes(is,System.out,1024, false);
IOUtils.closeStream(is);
}else{
System.err.println("文件不存在……");
}
}
▲ 例子六:上傳文件
/** 測試上傳 **/
@Test
public void testPutFile() throws Exception {
// 上傳
this.putFile("d://mail.txt","");
}
/**
* put 上傳文件
*
* @param fileName
*/
private void putFile(StringsrcPath,String destPath) throws Exception {
StringfileName = getFileName(srcPath);
if (!this.isFolderExist(destPath+ "/"+ fileName)) {
fs.copyFromLocalFile(new Path(srcPath), new Path(destPath==""?"/":destPath));
}else{
System.err.println("文件已經存在……");
}
}
/**
* 根據上傳的本地路徑,得到文件名
* @param srcPath
* @return
*/
private StringgetFileName(String srcPath) {
String[]arr = srcPath.split("//");
return arr[arr.length-1];
}
▲ 例子七:刪除文件或文件夾
/** 測試刪除文件或文件夾 **/
@Test
public void testDeleteFile() throws Exception {
// 刪除
this.deleteFile("/path");
}
/**
* 刪除文件
* @param fileName
*/
private void deleteFile(String filePath) throws Exception {
if (this.isFolderExist(filePath)){
boolean isDir = fs.getFileStatus(newPath(filePath)).isDir();
if(isDir){
System.out.println("正在刪除文件夾");
fs.delete(new Path(filePath), true);
}else{
System.out.println("正在刪除文件");
fs.delete(new Path(filePath), false);
}
}else{
System.err.println("文件不存在……");
}
}
例子八:測試獲取所有的文件夾或文件
/** 測試獲取所有的文件或文件夾 **/
@Test
public void testgetAllFiles() throws Exception {
this.getAllHDFSFiles(new Path("/"));
}
/**
* 得到所有的文件
*/
private void getAllHDFSFiles(Path path) throws Exception{
FileStatus[]fileStatus = fs.listStatus(path);
for(FileStatus fs :fileStatus){
//String name = fs.getPath().getName();
Stringtype = fs.isDir()?"目錄:":"文件:";
if(fs.isDir()){
System.out.println(type +fs.getPath());
this.getAllHDFSFiles(fs.getPath());
}else{
System.out.println(type +fs.getPath());
}
}
}
注:還可以測試修改文件的副本數,獲取文件的大小等。
★ RPC原理
RPC,即Remote ProcedureCall,即遠程過程調用。它是一種通過網絡從遠程計算機程序上請求服務,而不需要了解底層網絡技術的協議。RPC協議假定某些傳輸協議存在,如TCP UDP
RPC採用客戶機/服務機模式。請求程序就是一個客戶機,而服務提供程序就是一個服務器。
首先,客戶機調用進程發送一個有進程參數的調用信息到服務器進程,然後等待應答信息。在服務器端,進程保持睡眠狀態直到調用的信息到達爲止。當一個調用信息到達,服務器獲得進程參數,計算結果,發送答覆信息。然後等待焉個調用信息。
Hadoop的體系結構就是構建在RPC機制上的,如在啓動Hadoop的時候,start-all.sh會啓動所有的服務器端進程。也就是啓動RPC的服務器端。當我們用命令雲訪問HDFS或跪一個JOB的時候,其實就是客戶端在向服務器端發送請求。如果是訪問HDFS,則請求到達NameNode,NameNode再把請求分配到對應的DataNode。如果是一個MR的計算請求,則請求到達JobTracker,JobTracker會處理該請求,把任務分配到對應的TaskTracker。
下面是一個RPC的簡單原理實現。
▲ 1.定義一個接口
package com.broader.rpc;
import org.apache.hadoop.ipc.VersionedProtocol;
public interfaceMyBean extendsVersionedProtocol {
public abstract Stringhello(String name) ;
}
注:該接口繼承自VersionedProtocol,
該類的幾個子類:
ClientProtocol 它是客戶端FileSystem與NameNode通信和接口
DataNodeProtocol 它是DataNode與NameNode通信的的接口
NamenodeProtocol 它是SecondaryNameNode與NameNode通信的接口
該類結構如下:
▲ 2.定義上述接口的實現類
package com.broader.rpc;
import java.io.IOException;
public classMyBeanImpl implementsMyBean{
public static long VERSION = 2343555L;
@Override//被調用的方法
public String hello(Stringname) {
System.out.println("MyBeanImpl.hello method is called");
return "hello "+ name;
}
@Override
public longgetProtocolVersion(String protocol, long clientVersion)
throws IOException {
return VERSION;
}
}
▲ 3.定義服務器端
package com.broader.rpc;
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.ipc.RPC;
import org.apache.hadoop.ipc.Server;
public classMyServer {
public static final int PORT = 12345;
public static final String SERVER_ADDRESS= "localhost";
public static void main(String[] args) throws IOException {
//創建一個Server
final Server server = RPC.getServer(new MyBeanImpl(), SERVER_ADDRESS,PORT,
new Configuration());
//啓動Server 一直監聽12345端口
server.start();
}
}
▲ 4.定義客戶端
package com.broader.rpc;
import java.net.InetSocketAddress;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.ipc.RPC;
public classMyClient {
public static void main(String[] args) throws Exception{
//用接口來接收一個代理對象
final MyBean proxy =(MyBean)RPC.getProxy(
MyBean.class,
MyBeanImpl.VERSION,
newInetSocketAddress(MyServer.SERVER_ADDRESS,MyServer.PORT),
new Configuration());
//通過代理對象調用接口中定義的方法
StringreturnVal = proxy.hello("liuzhixiang");
System.out.println(returnVal);
//停止代理對象
RPC.stopProxy(proxy);
}
}
注:
先運行Myserver,啓動服務器端程序
再運行MyClient客戶端,則它會得到從服務器返回的信息。
RPC的調用類似於Socket的調用、也類似於WebService的調用