目录
一、HDFS常见命令
start-dfs.sh | 启动HDFS |
stop-dfs.sh | 结束HDFS |
hdfs --daemon start namenode | 启动NameNode |
hdfs --daemon start datanode | 启动DataNode |
hdfs --daemon start secondarynamenode | 启动SecondaryNameNode |
hadoop fs -put /home/a.txt / 或者 hadoopfs -copyFromLocal /home/a.txt / | 上传文件 |
hadoop fs -get /a.txt /home 或者 hadoop fs -copyToLocal /a.txt /home | 下载文件 |
hadoop fs -mkdir /txt | 创建目录 |
hadoop fs -mkdir -p /video/movie | 创建多级目录 |
hadoop fs -rm /b.txt | 删除文件 |
hadoop fs -rmdir /txt | 删除目录 |
hadoop fs -rm -r /video | 递归删除目录 |
hadoop fs -cat /c.txt | 查看文件内容 |
hadoop fs -tail /c.txt | 查看文件最后1000个字节的数据 |
hadoop fs -mv /c.txt /a.txt | 重命名或者剪切 |
hadoop fs -cp /txt/a.txt /a.txt | 复制文件 |
hadoop fs -ls / | 查看子文件或者子目录 |
hadoop fs -ls -R / | 递归查看 |
hadoop fs -setrep 3 /a.txt | 设置副本数量 |
hadoop fs -chmod 777 /a.txt | 更改权限 |
hadoop fs -chown tom /a.txt | 更改用户 |
hadoop fs -chgrp tedu /a.txt | 更改用户组 |
二、回收站机制
1、在HDFS中,回收站机制默认是不开启的,此时删除命令会立即生效,且该操作此时不可逆
2、配置回收站策略,配置在cores-site.xml中
<!--表示指定文件在回收站中的存放时间,单位是min-->
<!--如果超过指定的时间,依然没有将文件从回收站中还原回来-->
<!--回收站就会认为此时文件已经失效,就会清理掉-->
<!--如果不指定,那么此属性的值默认为0-->
<property>
<name>fs.trash.interval</name>
<value>1440</value>
</property>
3、回收站的默认存放位置为/user/root/.Trash/Current/
4、如果需要将文件从回收站中还原回来,那么使用hadoop fs -mv命令即可
三、HDFS流程
1、写(上传)流程
①、客户端发起RPC请求到NameNode,请求上传文件
②、当NameNode收到请求之后,会先进行校验
校验是否有指定路径 - FileNotFoundException
校验是否有写入权限 - AccessControlException
校验是否有同名文件 - FileAlreadyExistException
③、如果校验失败,则直接报错;如果校验成功,则NameNode会给客户端返回信号表示允许上传
④、当客户端收到信号之后,会再次给NameNode来发送请求,请求获取第一个Block的存储位置
⑤、NameNode收到请求之后,会将这个Block的存储位置(实际上是DataNode的IP或者主机名,默认情况下会返回3个存储位置 - 副本数量为3)返回给客户端
⑥、客户端收到存储位置之后,会从这些地址中选取一个较近(实际上是网络拓扑距离)的地址,发送请求,请求建立pipeline(管道,实际上是基于NIO Channel)用于传输数据;第一个Block所在节点会给下一个Block所在的节点发送请求,请求建立pipeline;依此类推,直到最后一个请求应答成功
⑦、建立好管道应答成功之后,客户端会将当前的Block进行封包,将Packet写入第一个节点;写完之后,第一个Block所在的节点写入第二个节点,依次类推
⑧、当这个Block的所有副本写完之后,客户端会再次给NameNode发送请求,请求获取下一个Block的存储位置,重复⑤⑥⑦⑧四个步骤,直到所有的Block全部写完
⑨、当客户端写完所有的Block之后,会给NameNode发送一个请求,请求关闭文件(关流)。文件一旦关闭,数据就不能修改
2、读(下载)流程
①、客户端发起RPC请求到NameNode,请求下载文件
②、NameNode收到请求之后,会先进行校验
校验是否有读取权限 - AccessControlException
校验是否有指定文件 - FileNotFoundException
③、如果校验失败,会直接报错;如果校验成功,则NameNode就会给客户端返回一个信号表示允许读取
④、客户端收到信号之后,会再次发送请求给NameNode,请求获取第一个Block的存储位置
⑤、NameNode收到请求之后,会查询元数据,然后将这个Block的存储地址(默认情况下是3个)返回给客户端
⑥、客户端收到地址之后,会从这些地址中选取一个较近的地址来读取这个Block
⑦、读取完这个Block之后,客户端会对这个Block进行checkSum校验。
如果校验失败,说明这个Block产生了变动,此时客户端会从剩余的地址中重新选取一个地址重新读取重新校验;
如果校验成功,则客户端会再次给NameNode发送请求,请求获取下一个Block的存储位置,重复⑤、⑥、⑦三个步骤,直到读取完所有的Block
⑧、当客户端读取完最后一个Block之后,会给NameNode发送一个结束信号。NameNode收到信号之后会关闭这个文件
3、删除流程
①、客户端发送RPC请求到NameNode,请求删除文件
②、NameNode收到请求后,会先进行校验
校验是否有删除权限 - AccessControlException
校验是否有指定文件 - FileNotFoundException
③、如果校验失败,则直接报错;如果校验成功,则NameNode会将这个写请求记录到edits_inprogress文件中,记录成功之后,会修改内存中的元数据;修改完成之后,NameNode会给客户端返回一个ACK信号表示删除成功。需要注意的是,此时文件并没有真正从HDFS上移除,仅仅是修改了元数据
④、NameNode给客户端返回信号之后,就会等待DataNode的心跳。NameNode在收到DataNode的心跳之后,会在心跳响应中要求DataNode删除对应的Block
⑤、DataNode在收到心跳响应之后,会按照NameNode的要求,去磁盘上删除文件对应的Block。注意,此时文件才真正的从HDFS上移除
四、HDFS的AP操作
1、首先创建maven工程
2、配置pox.xml
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!--单元测试-->
<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-common</artifactId>
<version>3.1.3</version>
</dependency>
<!--Hadoop客户端-->
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>3.1.3</version>
</dependency>
<!--Hadoop HDFS-->
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-hdfs</artifactId>
<version>3.1.3</version>
</dependency>
</dependencies>
3、在resources下创建log42.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>
4、新建HdfsDemo文件
package org.example.hdfs;
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 org.junit.Test;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URI;
public class HdfsDemo {
private final URI uri =URI.create("hdfs://192.168.186.128:9000");
private final Configuration conf = new Configuration();
//创建目录
@Test
public void mkdir() throws IOException, InterruptedException {
//构建环境变量
// Configuration conf = new Configuration();
//连接文件系统
FileSystem fs = FileSystem.get(uri,conf,"root");
//创建目录
fs.mkdirs(new Path("/txt"));
//关闭资源
fs.close();
}
//上传文件
@Test
public void put() throws IOException, InterruptedException {
//连接文件系统
FileSystem fs = FileSystem.get(uri,conf,"root");
//指定存储路径-返回输出流用于将文件写入
FSDataOutputStream out=fs.create(new Path("/txt/a.txt"));
//构建输入流,指定要上传的文件
FileInputStream in = new FileInputStream("D:/a.txt");
//上传文件
IOUtils.copyBytes(in,out,conf);
//关流
in.close();
out.close();
fs.close();
}
//下载文件
@Test
public void get() throws IOException {
//连接文件系统
FileSystem fs = FileSystem.get(uri,conf);
//指定要下载文件,获取到输入流用于读取数据
FSDataInputStream in = fs.open(new Path("/txt/a.txt"));
//构建输出流,用于将读取的数据写出
FileOutputStream out = new FileOutputStream("D:/b.txt");
//下载文件
IOUtils.copyBytes(in,out,conf);
//关闭资源
in.close();
out.close();
fs.close();
}
//删除文件
@Test
public void delete() throws IOException, InterruptedException {
//连接文件系统
FileSystem fs = FileSystem.get(uri,conf,"root");
//删除文件或目录
fs.delete(new Path("/txt"),true);
//关闭资源
fs.close();
}
}