1.NameNode和DataNode的作用
1.Nameode
1.1Namenode是一个中心服务器,负责管理文件系统的名字空间(namespace)以及客户端对文件的访问
1.2维护着文件系统树和整棵树内所有的文件和目录,这些信息以两个文件的形式永久的存放在本地磁盘上:命名空间镜像和编辑日志文件
对应的分别为日志文件和临时文件
在配置core-site.xml设置的便是临时文件存放的位置
在配置集群过程中,对从节点分发hadoop并配置好之后,需要删除对应的临时文件夹和日志夹的原因便是删除临时镜像文件,这么做的目的是删除文件系统的数据,便于启动进程时,是一个全新的文件系统
1.3namenode记录各个块所在的数据节点信息,但是不会永久保存块的位置信息,在系统启动过程中国有Datanode重建,而Datanode又由Namenode统一调度,Namenode的文件树的信息被保存到临时文件目录下
2.Datanode
Datanode节点的映射。Datanode负责处理文件系统客户端的读写请求。在Namenode的统一调度下进行数据块的创建、删除和复制,并定期向Namenode发送其所存储的块的列表
2.什么是管理者-工作者模式
HDFS集群的管理者-工作者模式是Namenode和Datanode之间的管理与实现
一个管理者(Namenode) 管理多个工作者(Datanode)
HDFS采用master/slave架构。一个HDFS集群是由一个Namenode和一定数目的Datanodes组成。Namenode是一个中心服务器,负责管理文件系统的名字空间(namespace)以及客户端对文件的访问。集群中的Datanode一般是一个节点一个,负责管理它所在节点上的存储。
这是为什么当主结点出现错误,没有Namenode时,HDFS文件系统无法使用。在实践中,主结点出错的机率时比较大的,这便需要对namenode实现容错
3.Hadoop的两种容错机制
备份
将持久状态写入到本地磁盘(写入本地临时文件),写入一个远程挂载的网络文件系统
辅助NameNode
运行一个辅助Namenode(# Secondary NameNode),但是其不能作为Namenode,不能将其理解为第二个Namenode
作用是定期通过编辑日志合并并命名空间镜像,以防止编辑日志过大,一般位于另一台机器上,并会在namenode发生错误时启动
hadoop集群搭建及编程实践中的hdfs-site.xml中的
这里的node01要更改为node02
4.命令行接口
4.1两个属性的理解
1.fs.default.name
在core-site.xml文件中,设置fs.default.name为hdfs:node01:9000
HDFS文件系统是由URL指定的,在配置文件中使用hdfs URL来配置HDFS为Hadoop的默认文件系统
HDFS的守护程序通过该属性来确定HDFS namenode的主机及端口,HDFS客户端可以通过该属性得知namenode在哪里运行进而连接到它
2.dfs.replication
在hdfs-site.xml文件中,设置dfs.replication为3
含义是指定文件系统块副本为3,HDFS会将块复制到3个datanode,根据hadoop集群搭建及编程实践可知hadoop集群规划中是有3个datanode的
默认为3,但是在伪分布式中,可以设置为1,因为此时只有一个datanode
当设置为3时,由于只有一个datanode,HDFS无法将块复制到3个datanode中,就会发出块副本不足的警告
超级用户是namenode进程的标识,对于超级用户,系统不会执行任何权限检查
4.2文件访问权限,超级用户
针对文件和目录,HDFS的权限模式和POSIX相似
一共提供三种权限模式:只读(r),写入(w),可执行(x)
读取文件或列出目录内容需要只读权限
写入一个文件或是在一个目录上新建及删除文件或目录,需要写入权限
对于文件而言,可执行权限可以忽略,因为不能再HDFS中执行文件,但在访问一个目录的子项时需要该权限
5.从Hadoop URL读取数据
要从HDFS文件系统中读取数据,最简单的是使用java.net.URL
import org.apache.hadoop.fs.FsUrlStreamHandlerFactory;
import org.apache.hadoop.io.IOUtils;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
/**
* @author prettyspider
* @ClassName Main
* @description: TODO
* @date 2023/10/9 11:50
* @Version V1.0
*/
public class UrlCat {
static {
URL.setURLStreamHandlerFactory(new FsUrlStreamHandlerFactory());
}
public static void main(String[] args) {
InputStream in = null;
try {
in=new URL(args[0]).openStream();
IOUtils.copyBytes(in,System.out,4096,false);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
源码
public static void setURLStreamHandlerFactory(URLStreamHandlerFactory fac) {
synchronized (streamHandlerLock) {
if (factory != null) {
throw new Error("factory already defined");
}
@SuppressWarnings("removal")
// 获取系统安茜接口
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkSetFactory();
}
handlers.clear();
// safe publication of URLStreamHandlerFactory with volatile write
factory = fac;
}
}
用URL识别Hadoop的hdfs URL还需要额外的工作,使用静态代码块的作用是设置URL工厂中已经有FsUrlStreamHandlerFactory()对象了,这个方法最多只能调用一次
由源码可知,在加锁的情况下,初始化时factory是null,会检查系统的安全接口,在最终会将传入的对象复制给类的成员变量factory,由于用到了锁,需要将其设置为静态
方法最多只能运行一次,在同一时间运行多个,就会抛出factory a异常lready define异常
打包后传输到虚拟机上,运行
jar下载提取码:fb7y
6.FileSystem API读取数据的三种方式及不同
由5知,URLStreamHandlerFactory有时是不可能被应用的,因此可以使用FileSystem API来打开一个文件的输入流
获取FileSystem实例有以下几种静态方法
public static FileSystem get(Configuration conf) throws IOException
public statuc FileSystem get(URI url,Configuration conf) throws OPException
public static FileSystem get(URI url,Configuration conf,String user) thorws IOException
Configuration 对象封装了客户端或服务器的配置,通过设置配置文件读取路径来实现
三种方法的区别:
1.第一种方法返回的是默认文件系统
2.第二种方法通过给定的URI方案和权限来确定要使用的文件系统,如果给定的URI中没有指定的方案,则返回默认文件系统
3.作为给定用户来访问文件系统,对安全来说至关重要
实现FileSystem API获取文件信息
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataInputStream;
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;
/**
* @author prettyspider
* @ClassName FileSystemCat
* @description: TODO
* @date 2023/10/9 20:53
* @Version V1.0
*/
public class FileSystemCat {
public static void main(String[] args) {
String hdfsUri = args[0];
String path = args[1];
String user = args[2];
Configuration conf = new Configuration();
FSDataInputStream in = null;
try {
FileSystem fs = FileSystem.get(URI.create(hdfsUri), conf, user);
in = fs.open(new Path(path));
IOUtils.copyBytes(in, System.out, 4096, false);
} catch (IOException e) {
throw new RuntimeException(e);
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
IOUtils.closeStream(in);
}
}
}
jar包下载提取码:kb69
7.在文件中获取指定位置的数据
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataInputStream;
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;
import java.net.URISyntaxException;
/**
* @author prettyspider
* @ClassName GetMessage
* @description: TODO
* @date 2023/10/9 21:11
* @Version V1.0
*/
public class GetMessage {
public static void main(String[] args) throws Exception {
FileSystem fs = FileSystem.get(new URI("hdfs://node01:9000"), new Configuration(), "prettyspider");
FSDataInputStream in=null;
try {
in = fs.open(new Path("input/hdfs-site.xml"));
IOUtils.copyBytes(in, System.out, 4096, false);
} catch (IOException e) {
throw new RuntimeException(e);
} catch (IllegalArgumentException e) {
throw new RuntimeException(e);
} finally {
IOUtils.closeStream(in);
}
}
}
8.FileSystem写入,读取数据的过程
在理解相应过程之前,需要提前知道的知识
1.什么是pipeline管道
Pipeline,中文翻译为管道。这是HDFS在上传文件写数据过程中采用的一种数据传输方式
客户端将数据块写入第一个数据节点,第一个数据节点保存数据之后再将块复制到第二个数据节点,后者保存后将其复制到第三个数据节点。
使用pipeline线性传输而不使用拓扑式传输的原因:
因为数据以管道的方式,顺序的沿着一个方向传输,这样能够充分利用每个机器的带宽,避免网络瓶颈和高延迟时的连接,最小化推送所有数据的延时 在线性推送模式下,每台机器所有的出口宽带都用于以最快的速度传输数据,而不是在多个接受者之间分配宽带
2.什么是ack应答相应
ACK (Acknowledge character)即是确认字符,在数据通信中,接收方发给发送方的一种传输类控制字符。表示发来的数据已确认接收无误
在HDFS pipeline管道传输数据的过程中,传输的反方向会进行ACK校验,确保数据传输安全。
3.默认3副本存储策略
第一块副本:优先客户端本地,否则随机
第二块副本:不同于第一块副本的不同机架
第三块副本:第二块副本相同机架不同机器
1.写入数据过程
1、HDFS客户端创建FileSystem对象实例DistributedFileSystem, FileSystem封装了与文件系统操作的相关方法。
2、调用DistributedFileSystem对象的create()方法,通过RPC请求NameNode创建文件。 NameNode执行各种检查判断:目标文件是否存在、父目录是否存在、客户端是否具有创建该文件的权限。检查通过,NameNode就会为本次请求记下一条记录,返回FSDataOutputStream输出流对象给客户端用于写数据。
3、客户端通过FSDataOutputStream开始写入数据。FSDataOutputStream是DFSOutputStream包装类。
4、客户端写入数据时,DFSOutputStream将数据分成一个个数据包(packet 默认64k),并写入一个内部数据队列(data queue)。 DFSOutputStream有一个内部类做DataStreamer,用于请求NameNode挑选出适合存储数据副本的一组DataNode,默认是3副本存储。DataStreamer将数据包流式传输到pipeline的第一个DataNode,该DataNode存储数据包并将它发送到pipeline的第二个DataNode。同样,第二个DataNode存储数据包并且发送给第三个(也是最后一个)DataNode。
5、DFSOutputStream也维护着一个内部数据包队列来等待DataNode的收到确认回执,称之为确认队列(ack queue),收到pipeline中所有DataNode确认信息后,该数据包才会从确认队列删除。
6、客户端完成数据写入后,在FSDataOutputStream输出流上调用close()方法关闭。
7、DistributedFileSystem联系NameNode告知其文件写入完成,等待NameNode确认。 因为namenode已经知道文件由哪些块组成(DataStream请求分配数据块),因此仅需等待最小复制块即可成功返回。 最小复制是由参数dfs.namenode.replication.min指定,默认是1.
2.读取数据过程
1、HDFS客户端创建FileSystem对象实例DistributedFileSystem, FileSystem封装了与文件系统操作的相关方法。调用DistributedFileSystem对象的open()方法来打开希望读取的文件
2、DistributedFileSystem使用RPC调用namenode来确定文件中前几个块的块位置(分批次读取)信息 对于每个块,namenode返回具有该块所有副本的datanode位置地址列表,并且该地址列表是排序好的,与客户端的网络拓扑距离近的排序靠前
3、DistributedFileSystem将FSDataInputStream输入流返回到客户端以供其读取数据。FSDataInputStream类是DFSInputStream类的包装
4、客户端在FSDataInputStream输入流上调用read()方法。然后,已存储DataNode地址的DFSInputStream连接到文件中第一个块的最近的DataNode
数据从DataNode流回客户端,结果客户端可以在流上重复调用read()
5、当该块结束时,DFSInputStream将关闭与DataNode的连接,然后寻找下一个块的最佳datanode。这些操作对用户来说是透明的
所以用户感觉起来它一直在读取一个连续的流
客户端从流中读取数据时,也会根据需要询问NameNode来检索下一批数据块的DataNode位置信息
6、一旦客户端完成读取,就对FSDataInputStream调用close()方法
9.在运行jar包时,命令行参数是如何导入
public static void main(String[] args)
args是一个数组,用户获取命令行的参数,索引从0开始
在运行jar包时,会在家暴之后传入一定数目的参数,参数会被args数组接受,在程序中通过索引获取参数,进而实现程序的运行
由可变参数可以将参数的形式变为
public static void main(String... args)
10.文件模式匹配
HDFS文件系统中的模式匹配和java的模式匹配类似,知识对应的正则需要在适当的位置