一、HDFS概述
- 组成架构
(1)NameNode
: 存储文件的元数据以及每个文件的快列表和块所在的DataNode
;
(2)DataNode
: 存储文件块数据以及校验和;
(3)Secondary NameNode
: 每隔一段时间对NameNode
元数据进行备份; HDFS
文件块大小
HDFS
中的文件在物理上是分块(Block
)存储,可以通过参数dfs.blocksize
(hdfs-default.xml)配置,在Hadoop2.x
以后的版本中,默认块大小是128M
。可以根据磁盘传输速率(mb/s
)配置最接近的块大小。
二、HDFS的Shell操作
-
基本语法:
hdfs dfs 操作命令
-
上传操作:
语法 说明 hdfs dfs -moveFromLocal local hdfs 将local剪切到hdfs中 hdfs dfs -appendToFile local-file hdfs 将local-file的内容追加到hdfs-file中 hdfs dfs -put local hdfs 将local复制一份到hdfs中
-
下载操作:
语法 说明 hdfs dfs -get hdfs local 将hdfs复制一份到local hdfs dfs -getmerge hdfs-files local-file 将hdfs-files的内容合并在一起,下载到本地,需要为合并后的文件命名
-
HDFS
直接操作(类Linux
命令):语法 说明 hdfs dfs -ls hdfs-path 展示某一文件信息或者某一目录下的文件列表信息 hdfs dfs -mkdir [-p] hdfs-path 创建目录,-p可以递归创建多级目录 hdfs dfs -cat hdfs-file 显示文件内容 hdfs dfs -chmod 744 hdfs-file 改变文件权限 hdfs dfs -chmod ([ugo]|a)[+-=][rwx] hdfs-file 改变文件权限 hdfs dfs -chown user-name[:group-name] hdfs-file 改变文件所属用户和用户组 hdfs dfs -cp hdfs-src hdfs-dest 复制 hdfs dfs -mv hdfs-src hdfs-dest 移动+改名 hdfs dfs -head hdfs-file 查看文件开头内容 hdfs dfs -tail hdfs-file 查看文件结尾内容 hdfs dfs -rm -r file-or-folder 删除文件或者文件夹 hdfs dfs -rmdir empty-folder 删除空目录 hdfs dfs -setrep hdfs-file 设置文件的副本数
三、HDFS
的Java API
操作
-
Windows
开发环境准备
(1) 将依赖下载并放置到任意位置上# 也可自行到github上下载 链接:https://pan.baidu.com/s/1cmrKssqrGlg0REw5RGMNdw 提取码:ehh7
(2) 将依赖目录下的
hadoop.dll
和winutils.exe
拷贝到C:\Windows\System32\
下
(3) 配置环境变量HADOOP_HOME
和PATH
变量名称 说明 例子 HADOOP_HOME 依赖路径 D:\moudle\hadoop-3.1.0 PATH PATH后添加:%HADOOP_HOME%\bin PATH后添加:%HADOOP_HOME%\bin
(4) 创建工程,引入依赖
<dependencies> <!-- junit测试依赖坐标 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <!-- 日志依赖坐标 --> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-slf4j-impl</artifactId> <version>2.12.0</version> </dependency> <!-- hadoop依赖坐标 --> <dependency> <groupId>org.apache.hadoop</groupId> <artifactId>hadoop-client</artifactId> <version>3.1.3</version> </dependency> </dependencies>
(5) 在
resources
目录下创建log4j
的配置文件<!-- 配置文件名为:log4j2.xml --> <?xml version="1.0" encoding="UTF-8"?> <Configuration status="error" strict="true" name="XMLConfig"> <Appenders> <!-- 类型名为Console,名称为必须属性 --> <Appender type="Console" name="STDOUT"> <!-- 布局为PatternLayout的方式, 输出样式为[INFO] [2018-01-22 17:34:01][org.test.Console]I'm here --> <Layout type="PatternLayout" pattern="[%p] [%d{yyyy-MM-dd HH:mm:ss}][%c{10}]%m%n" /> </Appender> </Appenders> <Loggers> <!-- 可加性为false --> <Logger name="test" level="info" additivity="false"> <AppenderRef ref="STDOUT" /> </Logger> <!-- root loggerConfig设置 --> <Root level="info"> <AppenderRef ref="STDOUT" /> </Root> </Loggers> </Configuration>
-
API操作
要导入的包为:import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.*; import java.net.URI;
(1) 获取
HDFS
客户端对象,所有的API
操作都必须基于该对象// NameNode的通信地址 URI uri = URI.create("hdfs://hadoop101:9820"); /* 配置对象 默认会从resources目录下读取配置文件:各种-site.xml文件 如果没有读取到配置文件,采用节点上的配置 也可以通过conf.set(key, value)方法指定配置 配置优先级:conf.set(key, value) > resources > 节点上的配置 > 默认配置 */ Configuration conf = new Configuration(); // 操作用户 String user = "soro"; // 获取客户端对象 FileSystem fs = FileSystem.get(uri, conf, user); /* todo */ // 释放资源 fs.close();
(2) 上传
/** * fs.copyFromLocalFile(delSrc, overwrite, src, dst); * 参数解读: * delSrc: * boolean, 是否删除源文件 * true=>删除、false=>不删除 * overwrite: * boolean, 当hdfs中已经存在要上传文件时,是否使用新文件覆盖旧文件 * true=>删除,false=>不删除 * src: * 要上传的文件 * dest: * 文件上传至hdfs的位置,需指定文件名 */
(3) 下载
/** * fs.copyToLocalFile(delSrc, src, dst, useRawLocalFileSystem); * 参数解读: * delSrc: * boolean, 是否删除源文件 * true=>删除、false=>不删除 * src: * 要下载的文件 * dst: * 文件下载至本地的位置,需指定文件名 * useRawLocalFileSystem: * 是否连同校验文件一并下载到本地 * true=>不下载, false=>下载 */
(4) 删除
/** * fs.delete(f, recursive); * 参数解读: * f: * 要删除的文件或者目录 * recursive: * boolean,是否递归删除 * true=>递归删除,false=>不递归删除 */
(5) 文件更名和移动
/** * fs.rename(src, dst) * 参数解读: * src: * 源文件 * dst: * 移动或者改名后的文件 */
(6) 获取文件详细信息
/** * fs.listFiles(root, recursive) * 参数解读: * root: * 根目录,从这个目录开始找文件 * recursive: * boolean,是否递归找文件 * true=>递归,false=>不递归 */ RemoteIterator<LocatedFileStatus> iterator = fs.listFiles(new Path("/"), true); while (iterator.hasNext()){ // 文件信息对象 LocatedFileStatus fileStatus = iterator.next(); // 可以使用文件信息的getXXX()方法,获取文件的详细信息 // todo // 获取文件的块信息 BlockLocation[] blockLocations = fileStatus.getBlockLocations(); // 可以调用每一个BlockLocation对象的getXXX()方法获取块的详细信息 // todo }
(7) 判断文件是否为目录
获取到文件的FileStatus
对象,调用其中的isDirectory()
和isFile()
方法进行判断,获取方法有:
powershell fs.listStatus() fs.listFiles(new Path("/"), true).next() fs.getFileStatus(new Path("path"))
3. 封装HDFS
客户端对象,简化部分操作
(1) 封装类名为:FSSharper
import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.FSDataInputStream; import org.apache.hadoop.fs.FSDataOutputStream; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.Path; import org.apache.hadoop.io.IOUtils; import java.io.IOException; import java.net.URI; /** * 简化获取FileSystem类的用法 */ public class FSSharper { /** * 异常类,把众多的其他异常简化为一个异常类 */ public static class FSSharperException extends Exception{ public FSSharperException(String tip){ super(tip); } /** * 抛出异常的静态方法 * @param tip 异常的提示信息 * @throws FSSharperException */ public static void throwException(String tip) throws FSSharperException { throw new FSSharperException(tip); } } // 文件系统对象 private FileSystem fs = null; // 输出流 private FSDataOutputStream fsDataOutputStream = null; // 输入流 private FSDataInputStream fsDataInputStream = null; /** * 构造方法 * @param conf 配置对象 * @throws FSSharperException */ public FSSharper(Configuration conf) throws FSSharperException { try { fs = FileSystem.get(conf); } catch (IOException e) { FSSharperException.throwException("异常发生于:public FSSharper(Configuration conf){...}"); } } /** * 构造方法 * @param uriString 文件系统对象要连接的址 * @param conf 配置对象 * @param user 用哪一个用户连接 * @throws FSSharperException */ public FSSharper(String uriString, Configuration conf, String user) throws FSSharperException { try { fs = FileSystem.get(URI.create(uriString), conf, user); } catch (Exception e) { FSSharperException.throwException("错误发生于:public FSSharper(String uriString, Configuration conf, String user){...}"); } } /** * 利用FileSystem对象创建文件的输出流 * @param path 文件路径 * @param overwrite 如果path已经存在,是否要覆盖,true表示覆盖,false表示不覆盖 * @return 输出流 * @throws FSSharperException */ public FSDataOutputStream getOutputStream(String path, boolean overwrite) throws FSSharperException { if (fsDataOutputStream != null) return fsDataOutputStream; try { fsDataOutputStream = fs.create(new Path(URI.create(path)), overwrite); } catch (Exception e) { FSSharperException.throwException("错误发生于:public FSDataOutputStream getOutputStream(String path, boolean overwrite){...}"); } return fsDataOutputStream; } /** * 利用FileSystem对象创建文件的输入流 * @param path 文件路径 * @return 输入流 * @throws FSSharperException */ public FSDataInputStream getInputStream(String path) throws FSSharperException { if (fsDataInputStream != null) return fsDataInputStream; try { fsDataInputStream = fs.open(new Path(URI.create(path))); } catch (IOException e) { FSSharperException.throwException("错误发生于:public FSDataInputStream getInputStream(String path){...}"); } return fsDataInputStream; } /** * 获取FileSystem对象 * @return 文件系统对象 */ public FileSystem getFs() { return fs; } /** * 获取创建该文件系统对象的配置对象 * @return 配置对象 */ public Configuration getConf(){ return fs.getConf(); } /** * 释放创建的输出流、输入流对象 */ public void closeStreams(){ IOUtils.closeStreams(fsDataOutputStream, fsDataInputStream); fsDataOutputStream = null; fsDataInputStream = null; } /** * 释放文件系统对象的资源 */ public void close(){ IOUtils.closeStream(fs); fs = null; } /** * 释放所有资源 */ public void closeAll(){ closeStreams(); close(); } }
(2) 使用示例
public static void main(String[] args) { // 创建FSSharper 对象 FSSharper fsSharper = null; try { fsSharper = new FSSharper(new Configuration()); // 获取客户端连接对象 FileSystem fs = fsSharper .getFs(); /* * todo */ } catch (FSSharper.FSSharperException e) { e.printStackTrace(); }finally { // 释放资源 fsSharper .closeAll(); } }