Hadoop 分布式文件系統(HDFS)是大數據生態存儲基石。本文詳細講解 Java API 訪問 HDFS 的核心配置文件解析、完整 CRUD 操作代碼、Kerberos 認證、高可用(HA)配置及生產環境調優參數。
hadoop.tmp.dir
/home/hadoop/hdfs/tmp
fs.defaultFS
hdfs://namenode-host:9000
io.file.buffer.size
131072
關鍵點:fs.defaultFS 必須指向 NameNode RPC 地址(端口 8020/9000),而非 HTTP WebUI 端口(50070/9870)。
dfs.namenode.name.dir
file:///mnt/disk1/dfs/nn,file:///mnt/disk2/dfs/nn
dfs.datanode.data.dir
file:///mnt/disk1/dfs/dn,file:///mnt/disk2/dfs/dn
dfs.replication
3
dfs.blocksize
134217728
dfs.client.read.shortcircuit
true
dfs.domain.socket.path
/var/run/hdfs-sockets/dn
dfs.nameservices
mycluster
dfs.ha.namenodes.mycluster
nn1,nn2
dfs.namenode.rpc-address.mycluster.nn1
nn1-host:8020
dfs.namenode.rpc-address.mycluster.nn2
nn2-host:8020
dfs.client.failover.proxy.provider.mycluster
org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider
純 HDFS 客戶端操作不需要這兩個文件。僅提交 MapReduce/YARN 作業時需配置。
org.apache.hadoop
hadoop-client
3.3.4
org.apache.hadoop
hadoop-hdfs
3.3.4
org.apache.hadoop
hadoop-common
3.3.4
package com.demo.hdfs;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.*;
import org.apache.hadoop.io.IOUtils;
import org.apache.hadoop.util.Progressable;
import java.io.*;
import java.net.URI;
public class HdfsClient {
private FileSystem fs;
private Configuration conf;
public HdfsClient(String namenodeUri, String user) throws IOException {
conf = new Configuration();
// 加載 classpath 下的 core-site.xml, hdfs-site.xml
// conf.addResource("core-site.xml"); // 可選,默認自動加載
fs = FileSystem.get(URI.create(namenodeUri), conf, user);
}
// 簡單構造:使用默認配置與當前用戶
public HdfsClient() throws IOException {
this("hdfs://namenode-host:9000", System.getProperty("user.name"));
}
/** 上傳文件 */
public void upload(String localPath, String hdfsPath, boolean overwrite) throws IOException {
Path src = new Path(localPath);
Path dst = new Path(hdfsPath);
fs.copyFromLocalFile(overwrite, src, dst);
// 或手動流式上傳(大文件、進度監聽)
// try (InputStream in = new BufferedInputStream(new FileInputStream(localPath));
// OutputStream out = fs.create(dst, new Progressable() {
// public void progress() { System.out.print("."); }
// })) {
// IOUtils.copyBytes(in, out, 4096, true);
// }
}
/** 下載文件 */
public void download(String hdfsPath, String localPath) throws IOException {
Path src = new Path(hdfsPath);
Path dst = new Path(localPath);
fs.copyToLocalFile(src, dst);
}
/** 追加寫入(需 HDFS 支援且配置 dfs.append.support=true) */
public void append(String hdfsPath, String content) throws IOException {
Path path = new Path(hdfsPath);
try (FSDataOutputStream out = fs.append(path)) {
out.write(content.getBytes("UTF-8"));
}
}
/** 讀取文件內容 */
public String readAsString(String hdfsPath) throws IOException {
Path path = new Path(hdfsPath);
try (FSDataInputStream in = fs.open(path);
ByteArrayOutputStream out = new ByteArrayOutputStream()) {
IOUtils.copyBytes(in, out, 4096, false);
return out.toString("UTF-8");
}
}
/** 刪除文件/目錄 */
public boolean delete(String hdfsPath, boolean recursive) throws IOException {
return fs.delete(new Path(hdfsPath), recursive);
}
/** 列出目錄 */
public FileStatus[] list(String hdfsPath) throws IOException {
return fs.listStatus(new Path(hdfsPath));
}
/** 創建目錄 */
public boolean mkdirs(String hdfsPath) throws IOException {
return fs.mkdirs(new Path(hdfsPath));
}
/** 文件/目錄是否存在 */
public boolean exists(String hdfsPath) throws IOException {
return fs.exists(new Path(hdfsPath));
}
/** 獲取文件元信息 */
public FileStatus getFileStatus(String hdfsPath) throws IOException {
return fs.getFileStatus(new Path(hdfsPath));
}
/** 關閉連接 */
public void close() throws IOException {
if (fs != null) fs.close();
}
// 使用示例
public static void main(String[] args) throws Exception {
try (HdfsClient client = new HdfsClient()) {
client.mkdirs("/user/test");
client.upload("local.txt", "/user/test/remote.txt", true);
System.out.println("文件內容: " + client.readAsString("/user/test/remote.txt"));
client.append("/user/test/remote.txt", "\n追加行");
for (FileStatus status : client.list("/user/test")) {
System.out.printf("名稱: %s, 大小: %d, 類型: %s%n",
status.getPath().getName(), status.getLen(),
status.isDirectory() ? "目錄" : "文件");
}
}
}
}
hadoop.security.authentication
kerberos
hadoop.security.authorization
true
import org.apache.hadoop.security.UserGroupInformation;
import javax.security.auth.login.LoginContext;
// 方式一:Keytab 免密登錄(推薦生產)
UserGroupInformation.setConfiguration(conf);
UserGroupInformation.loginUserFromKeytab("hdfs/host@REALM", "/path/to/hdfs.keytab");
FileSystem fs = FileSystem.get(conf);
// 方式二:票據緩存(開發測試)
// kinit hdfs@REALM 後
UserGroupInformation.setConfiguration(conf);
UserGroupInformation.loginUserFromSubject(null); // 使用默認 Subject
核心在於 fs.defaultFS 使用邏輯 nameservice:
Configuration conf = new Configuration();
conf.set("fs.defaultFS", "hdfs://mycluster"); // 對應 dfs.nameservices
conf.set("dfs.nameservices", "mycluster");
conf.set("dfs.ha.namenodes.mycluster", "nn1,nn2");
conf.set("dfs.namenode.rpc-address.mycluster.nn1", "nn1-host:8020");
conf.set("dfs.namenode.rpc-address.mycluster.nn2", "nn2-host:8020");
conf.set("dfs.client.failover.proxy.provider.mycluster",
"org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider");
FileSystem fs = FileSystem.get(conf);
// 自動故障轉移,無感知切換
| 參數 | 建議值 | 說明 |
|---|---|---|
| dfs.client.buffer.size | 131072 (128KB) | 讀寫緩衝區,大文件可調大 |
| dfs.client.read.shortcircuit | true | 同機短路讀,繞過 DataNode 進程 |
| dfs.client.read.shortcircuit.skip.checksum | false | 生產保持校驗,測試可關閉提速 |
| dfs.datanode.socket.write.timeout | 0 (無限) | 寫超時,防網絡抖動斷連 |
| ipc.client.connect.max.retries | 10 | RPC 重試次數 |
| ipc.client.connect.retry.interval | 3000 | 重試間隔 ms |
| hadoop.rpc.timeout | 60000 | RPC 超時 ms |
| 現象 | 原因 | 解決 |
|---|---|---|
| 連接超時 / Connection refused | fs.defaultFS 錯誤、防火牆、NameNode 未啟動 | 檢查端口 8020/9000、core-site.xml、telnet 測試 |
| No FileSystem for scheme: hdfs | 缺少 hadoop-hdfs 依賴、類加載衝突 | 引入 hadoop-hdfs、排除傳遞依賴衝突 |
| Permission denied | 用戶無權限、Kerberos 未認證、ACL 限制 | 檢查用戶組、kinit/keytab、HDFS 權限命令 |
| File could only be replicated to 0 nodes | DataNode 未啟動、磁盤滿、網絡不通 | 檢查 DataNode 日誌、磁盤空間、網絡連通性 |
| Lease recovery 卡住 | 客戶端異常退出未關閉流、NameNode 切換 | try-with-resources 確保關閉、配置租約恢復參數 |
| 短路讀不生效 | libhadoop.so 缺失、socket 路徑錯誤、用戶不一致 | 安裝 native lib、檢查 dfs.domain.socket.path、同用戶運行 |
掌握 HDFS Java API 的核心配置、流式操作模式、安全認證與 HA 故障轉移,是大數據開發者的必備技能。建議在測試環境完整跑通上傳、下載、追加、列表、刪除、權限、Kerberos、HA 切換全鏈路,再投入生產使用。