0 - 配置 Hadoop 環境(Windows系統)
下述步驟適用於 Windows 系統,其他系統可忽略。
在 Windows 系統直接執行 Hadoop 相關程式碼,會提示缺少 winutils.exe
和 hadoop.dll
檔案:
java.io.IOException: Could not locate executable null\bin\winutils.exe in the Hadoop binaries.
WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
原因:通過程式碼訪問 Hadoop 叢集,本地開發環境相當於 Hadoop 客戶端,需要有 Hadoop 相關軟體才可正常執行。
配置步驟:
1)到 https://github.com/cdarlint/winutils 下載與叢集版本相匹配的資料夾,然後將此資料夾拷貝到沒有中文和空格的路徑下,比如 D:\software\hadoop-3.2.1
;
2)在 Windows 的環境變數中新增 HADOOP_HOME
,值為上面的路徑,並將 %HADOOP_HOME%\bin
新增到 path
中;
3)把上述資料夾 bin目錄下的 hadoop.dll
檔案拷貝到系統盤 C:\Windows\System32
目錄;
4)重啟 Windows 電腦。
1 - 匯入 Maven 依賴
鑑於篇幅有限,相關 Maven 依賴請參見:《https://github.com/healchow/bigdata-study/blob/main/pom.xml》
2 - 常用類介紹
通過 Java API 操作 HDFS,主要涉及以下 class:
1)Configuration
主要用來封裝客戶端 / 服務端的配置。
2)FileSystem
這個類的物件是一個檔案系統物件,可以用該物件的一些方法來對檔案進行操作。
可通過靜態方法獲得該物件:
// 通過 conf 中的 “fs.defaultFS” 引數的值來確定檔案系統的具體型別
FileSystem fs = FileSystem.get(conf);
如果程式碼中沒有指定 fs.defaultFS
,並且工程的 ClassPath 下也沒有相應的配置,此引數的預設值就由 Hadoop Jar 包中的 core-default.xml
檔案來確定:
預設值是 file:///
,獲取的不是 DistributedFileSystem 的例項,而是一個本地檔案系統的客戶端物件。
3 - 常見 API 操作
3.1 獲取檔案系統(重要)
方式1:FileSystem.get(conf)
/**
* 獲取 FileSystem - FileSystem.get()
*/
@Test
public void testGetFileSystem1() throws IOException {
// 建立 Configuration 物件
Configuration conf = new Configuration();
// 指定檔案系統型別
conf.set("fs.defaultFS", "hdfs://hadoop:9000");
// 獲取指定的檔案系統
FileSystem fileSystem = FileSystem.get(conf);
// FileSystem fileSystem = FileSystem.get(new URI("hdfs://hadoop:9000"), new Configuration());
// 結果:DFS[DFSClient[clientName=DFSClient_NONMAPREDUCE_1219793882_1, ugi=healchow (auth:SIMPLE)]]
System.out.println(fileSystem);
// 關閉檔案系統
fileSystem.close();
}
方式2:FileSystem.newInstance(conf)
/**
* 獲取 FileSystem - FileSystem.newInstance()
*/
@Test
public void testGetFileSystem2() throws IOException {
// 建立 Configuration 物件
Configuration conf = new Configuration();
// 指定檔案系統型別
conf.set("fs.defaultFS", "hdfs://hadoop:9000");
// 獲取指定的檔案系統
FileSystem fileSystem = FileSystem.newInstance(conf);
// FileSystem fileSystem = FileSystem.newInstance(new URI("hdfs://hadoop:9000"), new Configuration());
System.out.println(fileSystem);
fileSystem.close();
}
3.2 建立目錄、寫入檔案
/**
* 通過 HDFS URL 建立目錄、寫入檔案
*/
@Test
public void testPutFile() throws IOException, URISyntaxException {
// 建立測試目錄(可建立多級目錄)
FileSystem fileSystem = FileSystem.newInstance(new URI("hdfs://hadoop:9000"), new Configuration());
boolean result = fileSystem.mkdirs(new Path("/test/input"));
System.out.println("mkdir result: " + result);
// 建立檔案,若存在則覆蓋,返回的是寫入檔案的輸出流
FSDataOutputStream outputStream = fileSystem.create(new Path("/test/input/hello.txt"), true);
String content = "hello,hadoop\nhello,hdfs";
outputStream.write(content.getBytes(StandardCharsets.UTF_8));
// 關閉流(不丟擲異常)
IOUtils.closeQuietly(outputStream);
}
3.3 上傳檔案
/**
* 向 HDFS 上傳檔案 - copyFromLocalFile()
*/
@Test
public void testUploadFile() throws URISyntaxException, IOException {
// 獲取 FileSystem
FileSystem fileSystem = FileSystem.get(new URI("hdfs://hadoop:9000"), new Configuration());
// 從本地上傳檔案,兩個引數都要指定到具體的檔案
fileSystem.copyFromLocalFile(new Path("/Users/healchow/bigdata/core-site.xml"),
new Path("/test/upload/core-site.xml"));
// 關閉FileSystem
fileSystem.close();
}
3.4 下載檔案
HDFS URL 開啟 InputStream 的方式:
/**
* 通過 HDFS URL 獲取檔案並下載 - IOUtils.copy() 方法
*/
@Test
public void testDownFileByUrl() throws IOException {
// 註冊 HDFS URL
URL.setURLStreamHandlerFactory(new FsUrlStreamHandlerFactory());
// 獲取 HDFS 檔案的輸入流
InputStream inputStream = new URL("hdfs://hadoop:9000/test/input/hello.txt").openStream();
// 獲取本地檔案的輸出流(絕對路徑,資料夾必須存在)
FileOutputStream outputStream = new FileOutputStream("/Users/healchow/bigdata/test/hello.txt");
// 拷貝檔案
IOUtils.copy(inputStream, outputStream);
// 關閉流(不丟擲異常)
IOUtils.closeQuietly(inputStream);
IOUtils.closeQuietly(outputStream);
}
FileSystem 開啟 InputStream 的方式:
/**
* 通過 FileSystem 獲取檔案並下載 - IOUtils.copy() 方法
*/
@Test
public void testDownloadFile() throws URISyntaxException, IOException {
// 獲取 FileSystem
FileSystem fileSystem = FileSystem.get(new URI("hdfs://hadoop:9000"), new Configuration());
// 獲取 HDFS 檔案的輸入流
FSDataInputStream inputStream = fileSystem.open(new Path("/test/input/hello.txt"));
// 獲取本地檔案的輸出流
FileOutputStream outputStream = new FileOutputStream("/Users/healchow/bigdata/test/hello1.txt");
// 拷貝檔案
IOUtils.copy(inputStream, outputStream);
// 關閉流
IOUtils.closeQuietly(inputStream);
IOUtils.closeQuietly(outputStream);
fileSystem.close();
}
FileSystem#copyToLocalFile() 的方式:
/**
* 通過 FileSystem 獲取檔案並下載 - copyToLocalFile() 方法
*/
@Test
public void testDownloadFileByCopyTo() throws URISyntaxException, IOException, InterruptedException {
// 獲取 FileSystem
FileSystem fileSystem = FileSystem.get(new URI("hdfs://hadoop:9000"), new Configuration(), "root");
// copyToLocalFile 拷貝檔案到本地,會下載 CRC 校驗檔案
fileSystem.copyToLocalFile(new Path("/test/input/hello.txt"),
new Path("/Users/healchow/bigdata/test/hello2.txt"));
// 關閉 FileSystem
fileSystem.close();
}
3.5 遍歷 HDFS 的檔案
/**
* 遍歷 HDFS 檔案
*/
@Test
public void testListFiles() throws URISyntaxException, IOException {
// 獲取FileSystem例項
FileSystem fileSystem = FileSystem.get(new URI("hdfs://hadoop:9000"), new Configuration());
// 遞迴獲取 /test 目錄下所有的檔案資訊
RemoteIterator<LocatedFileStatus> iterator = fileSystem.listFiles(new Path("/test"), true);
// 遍歷檔案
while (iterator.hasNext()) {
LocatedFileStatus fileStatus = iterator.next();
// 獲取檔案的絕對路徑:hdfs://hadoop:9000/xxx
System.out.println("filePath: " + fileStatus.getPath());
// 檔案的 block 資訊
BlockLocation[] blockLocations = fileStatus.getBlockLocations();
for (BlockLocation blockLocation : blockLocations) {
String[] hosts = blockLocation.getHosts();
for (String host : hosts) {
System.out.println("blockHost: " + host);
}
}
System.out.println("blockSize: " + blockLocations.length);
}
}
4 - HDFS 的訪問許可權控制
從上面的 API 練習,不難發現:只要得到了 HDFS 的 URL(即 fs.defaultFS
)配置項,能訪問到叢集的任何人都能讀寫 HDFS 上的資料,這會導致資料的安全性完全無法得到保障。
為了解決這個問題,HDFS 有 訪問許可權控制的方法,只有通過認證的使用者,按照其所擁有的許可權,讀取或寫入某些目錄下的檔案。
開啟 HDFS 訪問許可權控制的方法如下:
1)停止 HDFS 叢集:
cd ~/bigdata/hadoop-3.2.1
sbin/stop-dfs.sh
2)修改 ~/bigdata/hadoop-3.2.1/etc/hadoop/hdfs-site.xml
中的配置,新增如下內容:
<property>
<name>dfs.permissions.enabled</name>
<value>true</value>
</property>
4)重啟 HDFS 叢集:
cd ~/bigdata/hadoop-3.2.1
sbin/start-dfs.sh
5)上傳測試檔案到 HDFS 叢集,這裡將上傳後的一個檔案的許可權修改為 600
,即只能所有者讀寫:
cd ~/bigdata/hadoop-3.2.1/etc/hadoop
hdfs dfs -mkdir /test/config
hdfs dfs -put *.xml /test/config
hdfs dfs -chmod 600 /test/config/core-site.xml
6)通過程式碼下載檔案:
/**
* 通過下載檔案,測試訪問許可權控制
*/
@Test
public void testAccessControl() throws Exception {
// 開啟許可權控制後,當前使用者(啟動 NameNode 的使用者)應當能成功訪問
// FileSystem fileSystem = FileSystem.get(new URI("hdfs://hadoop:9000"), new Configuration());
// 偽造其他使用者訪問,應當訪問失敗
FileSystem fileSystem = FileSystem.get(new URI("hdfs://hadoop:9000"), new Configuration(), "testuser");
fileSystem.copyToLocalFile(new Path("/test/config/core-site.xml"),
new Path("file:/Users/healchow/bigdata/core-site.xml"));
fileSystem.close();
}
說明:本地測試失敗。無論用哪個使用者,訪問都成功。
查了很多資料,沒有說得通的。勞煩有了解的大佬,留言告知我呀?
版權宣告
出處:部落格園-瘦風的南牆(https://www.cnblogs.com/shoufeng)
感謝閱讀,公眾號 「瘦風的南牆」 ,手機端閱讀更佳,還有其他福利和心得輸出,歡迎掃碼關注?
本文版權歸博主所有,歡迎轉載,但 [必須在頁面明顯位置標明原文連結],否則博主保留追究相關人士法律責任的權利。