HDFS的设计简介:
-
超大文件:通常指具有几百MB,GB甚至TB大小的文件。
-
流式数据访问:一次写入,多次读取是最高效的访问模式。每次分析都将涉及该数据集的大部分甚至
-
全部数据,因此读取整个数据集比读取第一条记录的时间延迟更为重要。
-
商用硬件:Hadoop部署在普通硬件的集群上,因为节点故障率比较高
-
不支持低时间延迟的数据访问:HBase是更好的
-
大量的小文件:文件总数受限于namenode的内存容量,每个文件,目录和数据块的存储信息大致为150个字节。
-
不支持多用户写入,任意修改文件
-
HDFS以管理节点-工作节点模式运行,一个时NameNode(管理节点)和多个DataNode(工作节点),namenode管理文件的命名空间,维护者文件系统树及整棵树的所有文件和目录(永久保存在本地磁盘),同时记录着每个文件中各个块所在的数据节点信息(临时保存本地磁盘),因为会在系统启动时重新分配。datanode根据需要存储并检索数据块(受客户端namenode调度),并定期像namenode发送所存储的块信息。
-
SecondaryNameNode可以理解为namenode的备份,在namenode挂掉之后用于数据恢复,不过保存状态总是滞后于主节点。
-
数据块:默认为128M,HDFS的数据块比磁盘的块大的原因是最小化寻址开销,当块足够大时,寻址的时间就远小于磁盘传输数据的时间。
更详细的HDFS内容在后续博客中细细道来~
编译器:IDEA
需要用的工具:Maven, JUnit4.11
打开IDEA创建Maven的简单工程流程我就不说了,网上都是。
需要先配置pom.xml文件,把需要的jar包,JUnit,Hadoop版本属性,依赖等信息配置好,在进行开发
首先配置pom.xml文件,为了看的舒服点,我把它放在后面
pom.xml Maven配置文件
用Java API操作Hadoop文件系统与一般工厂模式相同,首先需要Configuration类,还需要FileSystem类,里面封装了hdfs的操作方法
- 先看看常用的命令行操作有哪些:
hadoop常用命令 |
---|
hadoop fs -ls [path] |
hadoop fs -put |
hadoop fs -copyFromLocal |
hadoop fs -moveFromLocal |
hadoop fs -cat |
hadoop fs -text |
hadoop fs -get |
hadoop fs -mkdir |
hadoop fs -mv |
hadoop fs -getmerge |
hadoop fs -rm |
hadoop fs -rmdir |
hadoop fs -rm -r |
hadoop fs -cp [file1/dir1] [file2/dir2] 与 hadoop distcp [file1/dir1] [file2/dir2]基本等价 |
我们用JUnit 测试这些API
首先将必要的,重复的连接操作封装起来:利用@Before, @After 注释分别在测试@Test前后执行
- 整个测试类需要的固定变量放在最前面
URL注意点: 有人可能习惯用hostname如我的hadoop000连接的,如果自己的系统是Windows的需要在文件C:\Windows\System32\drivers\etc\host 添加IP地址映射,若不进行配置,在Windows中只能使用虚拟机IP地址进行操作,不能使用hostname
//连接的URL
public static final String hadoopUrl = "hdfs://192.168.132.128:8020";
//连接的用户名
public static final String user = "hadoop";
Configuration configuration = null;
FileSystem fileSystem = null;
@Before
public void setUp() throws Exception {
System.out.println("=========set up=========");
configuration = new Configuration();
configuration.set("dfs.replication", "1");
fileSystem = FileSystem.get(new URI(hadoopUrl), configuration, user);
}
/**
* 完成测试后将configuration,filesystem置空回收内存
*/
@After
public void tearDown() {
configuration = null;
fileSystem = null;
System.out.println("=========tear down=========");
}
创建文件夹
- 返回值:boolean
源码如下:
/**
* 创建一个文件夹
* @throws Exception
*/
@Test
public void mkdir() throws Exception{
System.out.println(fileSystem.mkdirs(new Path("/hdfsapi/mkdir")));
}
- 测试成功,控制台输出:
- 查看根目录是否有叫mkdir的文件夹
- 还可以去游览器看
如果出现不能连接8020端口 [ java.net.ConnectException: Connection refused: connect.无法连接 ](出错的时候忘记截图了),需要检查8020端口是否打开,未打开需要打开端口,重新启动hdfs后再试一次!如果还是没解决最好看看官方文档:https://wiki.apache.org/hadoop/ConnectionRefused
创建文件
- 返回值:FSDataOutputStream
@Test
public void creat() throws IOException {
Path path = new Path("/hdfsapi/test/a.txt");
FSDataOutputStream out = fileSystem.create(path);
out.writeUTF("This is fileSystem.create() API TEST");
out.flush();
out.close();
}
注意:文件流走缓冲区,需要flush(),别忘了
看下里面的内容是不是上面写入的
去看看游览器的
- 多看几眼发现有点问题,这里的副本系数是 3 ,不过我记得设置的是 1,我们再去看看配置文件hdfs-site.xml中的dfs-repelication属性
- Configuration类中复本系数是自己默认的3没有走我们设置的属性,所以这里需要单独设置副本系数的属性,因为上面的我已经改过了,这里只是强调一下:这么做的目的是因为我们只有一个DataNode,所以如果是设置3个复本的会连续出现块复本不足的警告。
configuration.set("dfs.replication", "1");
测试一下dfs.replication属性,控制台输出
@Test
public void testReplication(){
System.out.println(configuration.get("dfs.replication"));
}
也可以新创建一个文件试试看,这里就变成了1了
删除文件
- 返回值:boolean
@Test
public void remove() throws Exception {
Path path = new Path("/hdfsapi/mkdir/repelication.txt");
fileSystem.delete(path, true);
}
重命名文件名
- 返回值:boolean
@Test
public void rename() throws Exception {
Path oldPath = new Path("/hdfsapi/test/a.txt");
Path newPath = new Path("/hdfsapi/test/aa.txt");
fileSystem.rename(oldPath, newPath);
}
拷贝本地文件到HDFS文件系统
- 返回值:void
@Test
public void copyFromLocalFile() throws Exception {
Path src = new Path("/Users/rocky/data/hello.txt");
Path dst = new Path("/hdfsapi/test/");
fileSystem.copyFromLocalFile(src,dst);
}
拷贝成功!
拷贝大文件到HDFS文件系统:带进度条
需要输入流与输出流,调用org.apache.hadoop.io.copyBytes(InputStream in, OutputStream out, int buffSize) 拷贝
@Test
public void copyFromLocalBigFile() throws Exception{
InputStream input = new BufferedInputStream(new FileInputStream(new File("E:\\ComputerQQDownload\\jdk-8u192-linux-x64.tar.gz")));
FSDataOutputStream out = fileSystem.create(new Path("/hdfsapi/test/jdk.tgz"), new Progressable() {
@Override
public void progress() {
System.out.print(">");
}
});
IOUtils.copyBytes(input,out,4096);
}
'>>>…'就是我们打印出来的简单进度条
拷贝HDFS文件到本地
@Test
public void copyToLocalFile() throws Exception {
Path src = new Path("/hdfsapi/test/aa.txt");
Path dst = new Path("D:\\");
fileSystem.copyToLocalFile(src, dst);
}
如果出现copyToLocalFile(Path src, Path dst)空指针异常,可能是Windows兼容性问题,换用:copyToLocalFile(boolean delSrc, Path src, Path dst, boolean useRawLocalFileSystem)
查看目标文件夹下的所有文件及状态
@Test
public void listSatue() throws Exception{
FileStatus[] status = fileSystem.listStatus(new Path("/hdfsapi/test/"));
for(FileStatus fileStatu : status){
String isDir = fileStatu.isDirectory() ? "dir" : "file";
String permission = fileStatu.getPermission().toString();
short replication = fileStatu.getReplication();
long len = fileStatu.getLen();
long blockSize = fileStatu.getBlockSize();
Path path = fileStatu.getPath();
System.out.println(isDir + "\t" + permission + "\t" + replication + "\t" + len + "\t"+ path + "\t" + blockSize);
}
}
}
递归查看目标文件夹下的所有文件
@Test
public void recurListFile() throws Exception {
RemoteIterator<LocatedFileStatus> iterator = fileSystem.listFiles(new Path("/hdfsapi/test/"), true);
while (iterator.hasNext()) {
LocatedFileStatus file = iterator.next();
System.out.println(file.isDirectory() ? "dir" : "file" + "\t" + file.getPermission() + "\t" + file.getLen() + "\t" + file.getPath());
}
}
查看hdfs某一文件所在的block状态(文件元数据FileStatus)
包括:文件长度,块大小,复本,修改时间,所有者以及权限信息
@Test
public void blockStatus() throws Exception {
FileStatus fileStatus = fileSystem.getFileStatus(new Path("/hdfsapi/test/jdk.tgz"));
BlockLocation[] blockLocations = fileSystem.getFileBlockLocations(fileStatus, 0, fileStatus.getLen());
for (BlockLocation bk : blockLocations) {
for (int i = 0; i < bk.getNames().length; i++)
System.out.println(bk.getNames()[i] + "\t" + bk.getLength() + "\t" + bk.getOffset() + "\t" + bk.getHosts()[i]);
}
}
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.bigdata</groupId>
<artifactId>hadoopLesson</artifactId>
<version>1.0-SNAPSHOT</version>
<name>hadoopLesson</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
<hadoop.version>2.6.0-cdh5.15.1</hadoop.version>
</properties>
<repositories>
<repository>
<id>cloudera</id>
<url>https://repository.cloudera.com/artifactory/cloudera-repos</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>${hadoop.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
<!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
<plugin>
<artifactId>maven-site-plugin</artifactId>
<version>3.7.1</version>
</plugin>
<plugin>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>3.0.0</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
常用的应该就这些吧 (=。=),欢迎来补充~