HDFS分布式文件系統資源管理器開發總結


 

HDFS,全稱Hadoop分布式文件系統,作為Hadoop生態技術圈底層的關鍵技術之一,被設計成適合運行在通用硬件上的分布式文件系統。它和現有的分布式文件系統有很多共同點,但同時,它和其他的分布式文件系統的區別也是很明顯的。HDFS是一個高度容錯性的系統,適合部署在連接的機器上。HDFS能夠提供高吞吐量的數據訪問,非常適合大規模數據集上的應用。

筆者本人接觸研究HDFS也有半年之久了,了解了HDFS Java API接口后,就一直設想着設計一個類似於Windows操作系統上的資源管理器一樣的小工具,用來管理分布式文件資源。其實,Hadoop Web UI和HUE都自帶有WEB版本的分布式文件瀏覽器,但是在文件信息管理方面不夠討人喜歡,可能筆者本人習慣了Windows上的資源管理器那樣的操作習慣。於是利用閑暇時間,經過幾個月斷斷續續的設計完善,最終開發出了一個類似Windows資源管理器一樣的管理工具,暫且就叫做“HDFS分布式文件資源管理器”,現在把設計思路過程總結一下,貼在網絡上,希望能夠對感興趣的朋友們有所幫助。成型后的資源管理器界面如下圖所示:

hdfs_browser

朋友們可以看出來,筆者本人是采用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包,特此說明下。下圖是開發工程結構圖:

hadoop_common_project

主要包括兩個工具類文件: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以使環境變量生效。

針對開發人員而言,我們推薦第三種解決方案。配置方法參考下圖:

hadoop_user_name

以下為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”,依次類推,等等等等。效果圖如下所示:

root_path1

root_path2

 

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是不允許直接刪除非空目錄的。

create_dir

rename_dir

delete_dir

 

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);

移動目錄文件有兩種操作方式,一是先打開目錄導航樹,選擇目標目錄,然后移動,如下圖所示;二是直接在目錄文件列表區域拖動要移動的目錄文件到左側目錄導航樹上,完成移動。

move_dir_file_dialog

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();

上傳效果圖如下所示:

upload_dialog

 

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();
下載效果圖如下所示:

download_dialog

 

3.7 目錄文件屬性

此功能窗體用於查看目錄文件的詳細屬性信息,對於目錄,則會統計此目錄所包含的子目錄和文件數量,以及目錄的總大小。效果圖如下所示:

property_dialog

 

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);
}

效果圖如下所示:

permission_dialog

 

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包文件。

lib_jars

還有,記着在環境變量中設置HADOOP_USER_NAME=hdfs,不然會出現權限不足方面的錯誤。配置完畢后,雙擊bat文件就可以啟動這個資源管理器了。

下載HDFS-Browser運行文件

 


 

作者:商兵兵

單位:河南省電力科學研究院智能電網所

QQ:52190634

主頁:http://www.cnblogs.com/shangbingbing

空間:http://shangbingbing.qzone.qq.com


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM