共计 12981 个字符,预计需要花费 33 分钟才能阅读完成。
HDFS,全称 Hadoop 分布式文件系统,作为 Hadoop 生态技术圈底层的关键技术之一,被设计成适合运行在通用硬件上的分布式文件系统。它和现有的分布式文件系统有很多共同点,但同时,它和其他的分布式文件系统的区别也是很明显的。HDFS 是一个高度容错性的系统,适合部署在连接的机器上。HDFS 能够提供高吞吐量的数据访问,非常适合大规模数据集上的应用。
笔者本人接触研究 HDFS 也有半年之久了,了解了 HDFS Java API 接口后,就一直设想着设计一个类似于 Windows 操作系统上的资源管理器一样的小工具,用来管理分布式文件资源。其实,Hadoop Web UI 和 HUE 都自带有 WEB 版本的分布式文件浏览器,但是在文件信息管理方面不够讨人喜欢,可能笔者本人习惯了 Windows 上的资源管理器那样的操作习惯。于是利用闲暇时间,经过几个月断断续续的设计完善,最终开发出了一个类似 Windows 资源管理器一样的管理工具,暂且就叫做“HDFS 分布式文件资源管理器”,现在把设计思路过程总结一下,贴在网络上,希望能够对感兴趣的朋友们有所帮助。成型后的资源管理器界面如下图所示:
朋友们可以看出来,笔者本人是采用 Eclipse+Swing 方式进行开发设计的,界面上没有动用第三方的 UI 组件,所以看起来稍显朴素,不过这都不是重点,关键是它在 Windows 和 Linux 下都运行良好,为笔者进行分布式文件管理提供了大大的便利。
1. 开发过程概述
笔者先大致讲述下此工具的开发过程。
第一步是封装了一下 HDFS Java API 接口。对组件系统提供的 API 接口方法进行二次封装好像已经成为了笔者本人的习惯了,如果不能按照自己的软件开发习惯调用接口方法,就总是感觉怪怪的。
第二步是功能模块设计。功能模块梳理比较轻松,一是因为自己的需求比较明确,二是因为有 Windows 资源管理器可以做参考。梳理后的主要功能包括几点:
目录导航树。类似 Window 资源管理器左侧的目录导航树。
目录文件列表。以 JTable 列表展示目录文件信息,类似于 Windows 资源管理器的 List 视图。
创建、重命名和删除目录。
重命名和删除文件。
上传文件(没有提供新建文件的功能)。
下载文件。
移动目录文件。
查看目录文件属性。
目录文件权限配置。
2. HDFS Java API 二次封装
对 HDFS Java API 进行二次封装,并不仅仅是为了设计开发 HDFS 分布式文件系统资源管理器,还要尽量考虑日后针对 HDFS 的其他后续开发,所以在封装的时候,尽量让自己目光看的远一些。
封装编译后的 jar 文件命名为 hnepri-hadoop-common.jar,里面也包含有针对 HBase Java API 的二次封装接口方法,所以命名为 hadoop-common 包,特此说明下。下图是开发工程结构图:
主要包括两个工具类文件:HadoopConfigUtil 和 HadoopFileUtil。其中,HadoopConfigUtil 为 HDFS 配置管理类,负责与 Hadoop 建立连接和信息配置;HadoopFileUtil 为 HDFS 文件目录操作工具类。
2.1. HadoopConfigUtil
HDFS 的核心配置文件是 core-site.xml 和 hdfs-site.xml,构建 Configuration 对象时读取这两个配置文件即可,如果有其他的自定义配置信息,可以将其配置在 hadoop.config.properties 文件。
另外需要特别强调的是,在 Windows 下利用 API 接口方法操作 HDFS 时会遇到权限认证的问题,类似“Permission denied: user=XXXXX,access=WRITE,inode=……”等一样的错误。这主要是由于当前用户与 HDFS 默认用户不一致所造成的,针对这种情况,有三种解决方案:
第一、在 hdfs 配置文件中,将 dfs.permissions 修改为 false,即取消 HDFS 的安全权限认证机制。
第二、在 hdfs 文件系统中为指定目录赋予当前用户操作的权限,譬如执行 hadoop fs -chmod 777 /user/hadoop 等。
第三、在环境变量中创建 HADOOP_USER_NAME 选项,其值为 HDFS 对应的用户名称,譬如 hadoop 或者 hdfs,然后重新启动计算机和 Eclipse 以使环境变量生效。
针对开发人员而言,我们推荐第三种解决方案。配置方法参考下图:
以下为 HadoopConfigUtil 类详细信息。
import java.io.File;
import java.util.HashMap;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.hdfs.DistributedFileSystem;
import com.hnepri.common.util.PropertiesUtil;
/**
* Description: Hadoop 信息配置工具类 <br>
* Copyright: Copyright (c) 2015<br>
* Company: 河南电力科学研究院智能电网所 <br>
* @author shangbingbing 2015-01-01 编写
* @version 1.0
*/
public class HadoopConfigUtil {
/**
* 加载解析 Hadoop 自定义配置信息。<br>
* 需在系统启动时调用此方法加载自定义配置信息,否则将采用默认配置或者无法连接 Hadoop。
*/
public static void loadHadoopConfigProperties() {
String path = “hadoop.config.properties”;
HashMap<String,String> pps = PropertiesUtil.readProperties(path);
HadoopConfigUtil.setHadoopConfigItemList(pps);
}
private static Configuration conf = null;
/**
* hadoop 配置信息列表, 其中 key 中存储参数名称, 譬如 master.hadoop;value 中存储参数值, 譬如 master.hadoop:9000 等
*/
private static HashMap<String,String> hadoopConfigItemList = new HashMap<String,String>();
/**
* 获取 hadoop 配置信息列表
* @return
*/
public static HashMap<String, String> getHadoopConfigItemList() {
return hadoopConfigItemList;
}
/**
* 设置 hadoop 配置信息列表
* @param hadoopConfigItemList
*/
public static void setHadoopConfigItemList(HashMap<String, String> hadoopConfigItemList) {
HadoopConfigUtil.hadoopConfigItemList = hadoopConfigItemList;
}
/**
* 添加 hadoop 配置信息
* @param key
* @param value
*/
public static void addHadoopConfigItem(String key,String value) {
if(hadoopConfigItemList.containsKey(key)) {
hadoopConfigItemList.remove(key);
}
hadoopConfigItemList.put(key, value);
}
/**
* 删除 hadoop 配置信息
* @param key
*/
public static void removeHadoopConfigItem(String key) {
if(hadoopConfigItemList.containsKey(key)) {
hadoopConfigItemList.remove(key);
}
}
/**
* 获取 Hadoop Configuration 对象
* @return
*/
public static Configuration getHadoopConfig() {
if(conf == null) {
conf = new Configuration();
try {
// 解决 winutils.exe 不存在的问题
File workaround = new File(“.”);
System.getProperties().put(“hadoop.home.dir”, workaround.getAbsolutePath());
new File(“./bin”).mkdirs();
new File(“./bin/winutils.exe”).createNewFile();
conf.addResource(“core-site.xml”);
conf.addResource(“hdfs-site.xml”);
// 初始化设置 zookeeper 相关配置信息
if(hadoopConfigItemList != null && hadoopConfigItemList.size() > 0) {
for(String key : hadoopConfigItemList.keySet()) {
String value = hadoopConfigItemList.get(key);
conf.set(key, value);
}
}
}
catch (Exception ex) {
System.out.println(ex.toString());
}
}
return conf;
}
/**
* 刷新重置 Hadoop 配置对象
*/
public static void initHadoopConfig() {
conf = null;
}
private static FileSystem fileSystem = null;
/**
* 获取 FileSystem 文件系统对象
* @return
*/
public static FileSystem getFileSystem() {
if(fileSystem == null) {
try {
fileSystem = FileSystem.get(getHadoopConfig());
} catch (Exception e) {
e.printStackTrace();
}
}
return fileSystem;
}
/**
* 获取 HDFS 文件系统对象
* @return
*/
public static DistributedFileSystem getHDFS() {
return (DistributedFileSystem)getFileSystem();
}
}
2.2. HadoopFileUtil
在 HadoopFileUtil 工具类文件中,主要包括本地目录文件和 HDFS 目录文件的操作接口方法,这里不再罗列详细的实现代码,笔者本人会以附件的形式将代码文件贴在文章后面,有需要的朋友请自行下载查看。这里对接口方法简单分类整理下。
1) 创建目录
创建 HDFS 目录。
根据本地目录结构在 HDFS 中创建对应的目录结构。
根据 HDFS 目录结构在本地创建对应的目录结构。
2) 复制目录文件
将本地文件复制(上传)到 HDFS 指定目录中。
将 HDFS 文件复制(下载)到本地指定目录中。
将本地目录文件按照目录结构复制(上传)到 HDFS 文件系统中。
将 HDFS 目录文件按照目录结构复制(下载)到本地文件系统中。
3) 重命名目录文件
重命名 HDFS 目录名称。
重命名 HDFS 文件名称。
4) 删除目录文件
删除 HDFS 文件。
删除 HDFS 目录及子目录和文件。
5) 获取 HDFS 目录文件
获取目录信息列表。
获取文件信息列表。
获取目录文件尺寸信息。
获取目录文件权限信息。
3. 功能模块设计
3.1 左侧目录导航树
利用 JTree 组件构建一个目录导航树是一件很容易的事情,不过需要注意的是,当 HDFS 文件系统中目录数量和层级较多时,该如何加载显示它们。通常有两种方式,一是一次性加载显示所有目录,这可能比较耗时,当然如果另外启动一个线程的话,在页面上也不会出现明显的阻塞。二是逐级加载显示目录,当用户点击某个目录时,系统才开始加载其下的子目录信息。
另外一个需要注意的细节,就是 JTree 目录树的刷新问题,当新建、重命名或者删除目录时,需要刷新 JTree 目录树,理想的方法是只刷新相关的 JTree 分支,而不是每次都刷新整棵目录树。
HDFS 文件系统的根目录路径是“/”,不过笔者本人在设计这个资源管理器时,并没有固定采用默认根目录路径,而是提供了可自定义的根目录路径接口,为什么要这样呢?因为我们考虑到日后要将此资源管理器组件嵌入到其他的窗口系统中,并可根据不同用户组权限分配设置不同的起始根目录,譬如,对于 admin 系统管理员等角色,根目录路径为”/”,对于 hdfs 角色用户,根目录路径则为“/user/hdfs”,依次类推,等等等等。效果图如下所示:
3.2 右侧目录文件列表
利用 JTable 组件构建右侧的目录文件列表,用不同的图标来区分文件和目录,列表中显示的内容包括:目录文件名称、文件大小、目录文件权限、所属用户、所属用户组、创建(修改)时间等。与 Windows 的资源管理器类似,系统不会显示目录大小信息,这是因为统计目录大小是一件比较耗时的工作。
在列表中双击目录行时,系统将打开进入此目录。但双击文件行时,系统将不执行任何操作。在这一点,朋友们可以根据自己的需要自行开发设计。
目录文件的权限信息格式与 linux 系统中的目录文件权限信息格式类似,HDFS Java API 提供有接口方法获取和设置权限信息,笔者本人编写了一个方法,专门用来解析获取目录文件的权限信息,代码如下:
/**
* 解析文件权限信息
* @param fs
* @return
*/
public static String getFilePermissionInfo(FileStatus fs) {
String fileType = “-“;
if(fs.isDirectory()) {
fileType = “d”;
} else if (fs.isSymlink()) {
fileType = “l”;
}
return fileType + fs.getPermission().toString();
}
3.3 管理目录文件
创建目录、重命名目录以及重命名文件的代码都比较简单明了,这里不再赘述,下面只贴出来几张效果图供朋友们参考。当删除目录时,需要先删除目录中的文件,然后才能删除目录,也就是说,HDFS 是不允许直接删除非空目录的。
3.4 移动目录文件
移动目录文件其实是重命名目录文件的变相操作,是在保持目录文件名称不变的同时改变下目录文件路径而已。当移动一个目录时,会同时移动此目录下的所有子目录和文件。譬如移动某个文件,示例代码如下:
Path srcPath = new Path(“/user/hdfs/2015/10/10.dat”);
Path dstPath = new Path(“/user/hdfs/2014/08/10.dat”);
HadoopConfigUtil.getFileSystem().rename(srcPath, dstPath);
移动目录文件有两种操作方式,一是先打开目录导航树,选择目标目录,然后移动,如下图所示;二是直接在目录文件列表区域拖动要移动的目录文件到左侧目录导航树上,完成移动。
3.5 上传目录文件
上传目录文件,是指在本地文件系统中选择目录文件,将其上传到 HDFS 系统中。如果上传的是文件,则直接将其上传到 HDFS 指定目录中即可;如果上传的是目录,则需要根据本地目录结构在 HDFS 系统中构建对应的目录结构,然后将文件上传到对应的目录中。
HDFS 文件系统中存储的一般都是大文件数据,因此在上传或者下载的时候必须有进度提醒。
下面,笔者将采用截图、代码的形式讲解下目录文件上传的大致流程。
第一,选择本地文件系统中要上传的目录文件,可一次上传多个目录文件。
JFileChooser chooser = new JFileChooser();
chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
chooser.setMultiSelectionEnabled(true);
chooser.showDialog(this, “ 选择目录或文件 ”);
if(chooser.getSelectedFiles().length == 0) return;
File[] files = chooser.getSelectedFiles();
第二,解析已选择的本地文件,将它们罗列在 JTable 列表中,以方便上传监控。
第三,根据已选择的本地目录,在 HDFS 系统中构建对应的目录结构。
第四,循环读取 JTable 文件列表,逐个上传文件,并实时更新上传进度。关键代码如下所示:
new Thread(new Runnable() {
@Override
public void run() {
int rowCount = tableModel.getRowCount();
for(int i=0;i<rowCount;i++) {
final int rowIndex = i;
String localFilePath = tableModel.getValueAt(rowIndex, 1).toString();
String hdfsFilePath = pathMappingList.get(localFilePath);
InputStream in = null;
OutputStream out = null;
try {
File localFile = new File(localFilePath);
final int fileSize = (int)localFile.length();
final int[] uploadSize = new int[1];
final DecimalFormat df = new DecimalFormat(“#”);
in = new BufferedInputStream(new FileInputStream(localFilePath));
out = HadoopConfigUtil.getFileSystem().create(new Path(hdfsFilePath),new Progressable() {
public void progress() {
uploadSize[0] += 1024*64;
double dblPercent = (uploadSize[0] * 1.0 / fileSize) * 100;
String strPercent = df.format(dblPercent);
tableModel.setValueAt(strPercent + “%”, rowIndex, 4);
}
});
IOUtils.copyBytes(in, out, 1024*64, true);
tableModel.setValueAt(“ 已上传 ”, rowIndex, 4);
} catch (Exception ex) {
ex.printStackTrace();
} finally {
if(in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
}).start();
上传效果图如下所示:
3.6 下载目录文件
下载目录文件,是指在 HDFS 文件系统中选择目录文件,将其下载到本地文件系统中。如果下载的是文件,则直接将其下载到本地指定目录中即可;如果下载的是目录,则需要根据 HDFS 系统目录结构在本地系统中构建对应的目录结构,然后将文件下载到对应的目录中。
下面,笔者将采用截图、代码的形式讲解下目录文件下载的大致流程。
第一,选择 HDFS 文件系统中要下载的目录文件,可一次下载多个目录文件。
第二,解析已选择的 HDFS 文件,将它们罗列在 JTable 列表中,以方便下载监控。
第三,根据已选择的 HDFS 目录,在本地文件系统中构建对应的目录结构。
第四,循环读取 JTable 文件列表,逐个下载文件,并实时更新下载进度。关键代码如下所示:
new Thread(new Runnable() {
@Override
public void run() {
int rowCount = tableModel.getRowCount();
for(int i=0;i<rowCount;i++) {
String hdfsFilePath = tableModel.getValueAt(i, 1).toString();
String localFilePath = pathMappingList.get(hdfsFilePath);
Path path = new Path(hdfsFilePath);
InputStream in = null;
OutputStream out = null;
try {
FileStatus fs = HadoopConfigUtil.getFileSystem().getFileStatus(path);
int fileSize = (int)fs.getLen();
in = HadoopConfigUtil.getFileSystem().open(path, 1024);
byte[] buffer = new byte[fileSize];
int offset = 0;
int numRead = 0;
double dblPercent = 0;
DecimalFormat df = new DecimalFormat(“#”);
while(offset < buffer.length && (numRead = in.read(buffer,offset,buffer.length – offset)) >= 0) {
offset += numRead;
dblPercent = (offset * 1.0 / fileSize) * 100;
String strPercent = df.format(dblPercent);
tableModel.setValueAt(strPercent + “%”, i, 4);
}
if(offset != buffer.length) {
throw new IOException(“ 不能完整地读取文件 ” + hdfsFilePath);
}
tableModel.setValueAt(“ 已下载 ”, i, 4);
File localFile = new File(localFilePath);
if(localFile.getParentFile().exists() == false) {
localFile.getParentFile().mkdirs();
}
out = new FileOutputStream(localFile);
out.write(buffer);
out.flush();
} catch (Exception ex) {
ex.printStackTrace();
} finally {
if(in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
}).start();
下载效果图如下所示:
3.7 目录文件属性
此功能窗体用于查看目录文件的详细属性信息,对于目录,则会统计此目录所包含的子目录和文件数量,以及目录的总大小。效果图如下所示:
3.8 配置目录文件权限
配置目录文件权限是一个高级别的功能,一般只开放给管理员用户,普通用户是禁用的。权限配置分两部分:文件权限和用户权限。
文件权限配置代码如下所示:
int userPermission = 7;
int groupPermission = 6;
int otherPermission = 6;
String permissionInfo = userPermission + “” + groupPermission + “” + otherPermission;
for(String filePath : filePathList) {
Path path = new Path(filePath);
FsPermission permission = new FsPermission(permissionInfo);
HadoopConfigUtil.getFileSystem().setPermission(path, permission);
}
用户权限配置代码如下所示:
String userName = this.txtOwner.getText();
String groupName = this.txtGroup.getText();
for(String filePath : filePathList) {
Path path = new Path(filePath);
HadoopConfigUtil.getFileSystem().setOwner(path, userName, groupName);
}
效果图如下所示:
4. HDFS-BROWSER 编译运行文件
笔者以“/user/hdfs”为根目录路径,编译导出了一个简单版本的 HDFS-BROWSER 运行文件包,各位朋友可以下载试运行一下。当然,前提是你要有一个 Hadoop 集群才行(本地模式、伪集群都可以,只要有 HDFS 服务就行),并用你的集群配置文件 core-site.xml 和 hdfs-site.xml 进行替换;另外,笔者本人是用 JDK7 进行编译的。
本来想把所有的 jar 都一块打包算了,不过 hadoop 相关的 jar 包实在太多了,取舍又很不方便,所以只保留了几个笔者本人自己开发的 jar 包文件,其他 jar 文件都删除了,需要您手动把 hadoop 相关的 jar 包文件拷贝到 lib 目录中,这里给出相关 jar 包文件的清单截图,仅供参考,如果您有耐心的话,可以剔除不需要的 jar 包文件。
还有,记着在环境变量中设置 HADOOP_USER_NAME=hdfs,不然会出现权限不足方面的错误。配置完毕后,双击 bat 文件就可以启动这个资源管理器了。
下载 HDFS-Browser 运行文件
—————————————— 分割线 ——————————————
免费下载地址在 http://linux.linuxidc.com/
用户名与密码都是 www.linuxidc.com
具体下载目录在 /2016 年资料 / 1 月 /10 日 /HDFS 分布式文件系统资源管理器开发总结 /
下载方法见 http://www.linuxidc.com/Linux/2013-07/87684.htm
—————————————— 分割线 ——————————————
Hadoop 如何修改 HDFS 文件存储块大小 http://www.linuxidc.com/Linux/2013-09/90100.htm
将本地文件拷到 HDFS 中 http://www.linuxidc.com/Linux/2013-05/83866.htm
从 HDFS 下载文件到本地 http://www.linuxidc.com/Linux/2012-11/74214.htm
将本地文件上传至 HDFS http://www.linuxidc.com/Linux/2012-11/74213.htm
HDFS 基本文件常用命令 http://www.linuxidc.com/Linux/2013-09/89658.htm
Hadoop 中 HDFS 和 MapReduce 节点基本简介 http://www.linuxidc.com/Linux/2013-09/89653.htm
《Hadoop 实战》中文版 + 英文文字版 + 源码【PDF】http://www.linuxidc.com/Linux/2012-10/71901.htm
Hadoop: The Definitive Guide【PDF 版】http://www.linuxidc.com/Linux/2012-01/51182.htm
更多 Hadoop 相关信息见 Hadoop 专题页面 http://www.linuxidc.com/topicnews.aspx?tid=13
本文永久更新链接地址 :http://www.linuxidc.com/Linux/2016-01/127259.htm