hadoop之HDFS学习笔记(二)

主要内容:hdfs的核心工作原理:namenode元数据管理机制,checkpoint机制;数据上传下载流程

1、hdfs的核心工作原理

1.1、namenode元数据管理要点

1、什么是元数据?

hdfs的目录结构及每一个文件的块信息(块的id,块的副本数量,块的存放位置<datanode>)

 

2、元数据由谁负责管理?

namenode

 

3、namenode把元数据记录在哪里?

  试想一下,如果元数据是以文件的形式存在和管理的,会很不方便,因为文件是一个顺序的结构,当用户新上传或者,移动,删除hdfs中的文件是,对文本元数据的维护会变得很麻烦。

  实际上元数据是放在内存中的,用java对象来表示,使用树(精心设计的数据结构对象)表示目录结构,这样会很方便对元数据进行管理。放到内存就会遇到一个严重的问题,万一宕机或者断电,我们知道内存中的数据是不可逆的,会丢失,所以还需要定期的保存一份备份数据在硬盘上,在namenode工作目录下形成一个特定文件。那么序列化时机呢?

  如果元数据一发生变化就将其序列化到硬盘,这显然是不妥的,因为元数据信息可能很大,硬盘难以承受频繁的写操作,所以只能定期的进行序列化,这样又带来一个问题,在这个期间内存中的元数据可能发发生变化,如果在这个期间宕机,下次启动,从硬盘恢复过来的数据就不是最新的,怎么办?

  现在遇到的问题是,内存中的数据是实时更新的,硬盘中的数据没法实时更新,通用解决方案,(任何地方碰到类似的情况下也适用):一旦用户发出修改内存数据的请求,应当在修改内存数据完成后,将本次的修改操作记录下来(以日志的形式,日志不存在修改的问题,只是追加),形成文件,保存在硬盘。这样宕机重启之后,将上一次序列化的文件反序列化,然后解析日志,将反序列化结果按照日志解析结果进行相应的数据修改,恢复到上次宕机是的状态。

  日志采用滚动记录形式,确保不会出现很大的日志文件,避免单机重启的时候,重放日志花去太多的时间,还有一个问题,若宕机之前,namanode正常运转了很长一段时间,形成了许许多多的日志块,若下次重启机器的时候从最老的日志开始重放吗?若是这样同样会花去很多时间。如何改进呢?

  解决办法,不必等到宕机重启的时候去重放日志来恢复,定期重放日志,将日志和fsimage合并,将合并完的日志删除,并对合并结果进行编号(编号参考合并日志中最大(最新)的编号);为了保证namnode工作性能,专心管理元数据,这整个过程由secondary name node来完成。

  secondary namenode 第一次从namenode拉去fsimage_00文件和edits_00 --- edits_95(最新)日志,到secondary 的本地,然后发序列化fsimage_00在自己的内存中形成元数据对象,然后重放日志endits_00 --edits_95修改内存中的元数据对象,然后将修改好的元数据对象序列化到自己的硬盘中,形成fsimage_95文件,然后将fsimage_95文件上传到namenode工作目录下;此时namenode的工作目录下会同时并存fsimage_00和fsimage_95(保存最新的两个版本);然后secondary namenode 继续讲编号95之后的日志(比如edits_96 --- edits_195)拉取到本地,不会再拉取fsimage(只会在第一次操作的时候拉取fsimage),然后继续将本地的fsimage_95 和 最新的日志edits_96 --- edits_195合并,最终形成fsimage_195,上传给namenode,同时删除最老的fsimage_00。 默认每隔一个小时做一次合并操作。整个机制就是checkpoint(在某个检查点形成新的状态)。下次namenode宕机重启的时候,会将自己工作目录下的fsimage反序列化并重返新的日志。

 

 

 

 

namenode的实时的完整的元数据存储在内存中;

namenode还会在磁盘中(dfs.namenode.name.dir)存储内存元数据在某个时间点上的镜像文件;

namenode会把引起元数据变化的客户端操作记录在edits日志文件中;

 

secondarynamenode会定期从namenode上下载fsimage镜像和新生成的edits日志,然后加载fsimage镜像到内存中,然后顺序解析edits文件,对内存中的元数据对象进行修改(整合)

整合完成后,将内存元数据序列化成一个新的fsimage,并将这个fsimage镜像文件上传给namenode

 

上述过程叫做:checkpoint操作

提示:secondary namenode每次做checkpoint操作时,都需要从namenode上下载上次的fsimage镜像文件吗?

第一次checkpoint需要下载,以后就不用下载了,因为自己的机器上就已经有了。

 

补充:secondary namenode启动位置的配置

 

<property>

  <name>dfs.namenode.secondary.http-address</name>

  <value>0.0.0.0:50090</value>

</property>

 

把默认值改成你想要的机器主机名即可

 

secondarynamenode保存元数据文件的目录配置:

<property>

  <name>dfs.namenode.checkpoint.dir</name>

  <value>file://${hadoop.tmp.dir}/dfs/namesecondary</value>

</property>

 

改成自己想要的路径即可:/root/dfs/namesecondary

 

2、写数据流程

datanode会定期的向namenode回报自身持有的block信息,如果与元数据记录信息不一致,会少补多删。假如客户端在传最后一块block的时候出现异常,会通知namenode文件上传失败,那么清空该上传文件的元数据,此时datanode中存在的已经上传好的block会在定期向namenode汇报之后删除。

3、读数据流程

 

4、hadoop的HA机制原理解析

namenode程序是带状态的,所有两个节点之间的同步就比较复杂(standby需要继承挂掉的节点的状态)。yarn集群的HA就没有这么复杂(没有状态继承,只需要在Zookeeper中记录谁是active就可以)

两点:

  1、元数据如何同步

  2、状态如何切换

 Zookeeper—学习笔记(一)

 

4.1、Qjournal分布式日志管理系统--集群

Q:QuorumPeerMain(Zookeeper的进程名字)

分布式日志管理系统(集群)Qjournal(提供数据的可靠保存,半数以上节点存活就可以正常对外提供服务;与Zookeeper的数据同步策略相同——paxos算法做数据一致性同步:奇数节点比较合适,半数以上节点存活系统就可正常工作

active Namenode一方面往自己的本地记录日志edits,另一方面往Qjournal记录日志

standby Namenode 不断的从Qjournal中读取日志文件edits,一方面解析日志跟新内存中的元数据,一方面更新自己的fsimage文件,同时将跟新后的fsimage文件上传到(承担了secondnary NameNode的作用——合并fsimage和edits文件)active namenode,覆盖其原来的fsimage

4.2、zkfc:状态协调功能

 

activeNamenode 和 standby namenode 之间的状态协调功能;

比如:

  1、不能起冲突,active是谁,已经有了active,那么另外一个节点只能是standby;

  2、active挂了,standby 得知道,standby要不切换状态

zkfc的由来:namenode的逻辑本身已经十分复杂了,若是在将状态切换功能考虑进去,会更加的复杂,需要推到重做。hadoop开发组将这些状态协调管理功能封装到了另外的程序中,叫做zkfc(Zookeeper failover controller)基于Zookeeper的失败/故障切换/转移控制器。

1、zkfc以本地进程的方式来检测namenode

2、zkfc之间会通过Zookeeper来交换信息

zkfc会在Zookeeper上记录一些状态协调的信息以及注册监听

  1、比如谁是active 状态的namenode;

  2、比如哪个namenode挂了

 

4.3、脑裂

集群中出现了两个active 的nameNode;

可能的原因:zkfc是一本地进程的方式与nameNode交互,若收不到nameNode的回应,会认为nameNode挂了,就会通知另外一台机制上的zkfc,另外一台机子上的zkfc收到消息后,如果立即将本机的standby namenode切换为active namenode,便有产生脑裂的风险;因为事实上,原来的active namenode没有反应不等于就挂了,因为java程序是运行在jvm上的,而jvm的GC机制可能会stop the world(通俗解释,GC平常都是做小范围的垃圾回收,这样肯定无法清理干净,当垃圾积攒到一定程度后会出发Full GC,jvm会停止所的用户线程,去清理垃圾),这样其实原来的active并没有挂掉,而此时的standby 已经切换成了active 转台,当GC做完之后,原来被jvm冻结的active恢复正常,此时就出现了两个ActiveNameNode,这就是脑裂。

zkfc防止脑裂的策略有两种:

  1、ssh kill

  2、shell 脚本

standby 远程登录原来的active namendoe 执行kill -9命令,若原active有回应,表示杀死了原active的进程,此时standby放心大胆的上位成standby;如果远程ssh kill没有在一个超时时间之内没有反应,则指向用户提供的shell脚本(警告也好,断掉原active的与网络也好,电源也好)。脚本返回0/true,则放心去切换状态。

4.4、name service

一对儿的active Namenode 和 standby nameNode术语叫做name service(名称服务)。

5、hadoop的HA集群搭建

 最小的HA集群规划

 5.1、修改core-site.xml

非HA机制下的core-site.xml配置,指定了namenode以及端口号;而HA机制下不能用url来写死namenode,因为HA机制下是两台nameNode不知道哪一台是active,namenode是会随时切换,如果写死了,客户端只会链接uri中指定的namenode;所以HA机制下只写 name service(代表着一对namenode,name service的名字是自己定的,后面一定会有配置文件来解释这个name service,究竟是哪两个namenode)

<name>fs.defaultFS</name>
<value>hdfs://hdp-01:9000/</value>

 

 HA机制下的namenode;

HA机制下的两个namenode之间的切换需要依赖Zookeeper,所以还需要配置Zookeeper

<configuration>
<!-- 指定hdfs的nameservice为ns1 -->
<property>
<name>fs.defaultFS</name>
<value>hdfs://hdp24/</value>
</property>
<!-- 指定hadoop临时目录 -->
<!-- nodemanager 作为容器提供者,会提供容器,运行用户提交的程序,这个过程会产生一些临时的数据,若不配置默认存放在temp目录; --> <property> <name>hadoop.tmp.dir</name> <value>/root/hdptmp/</value> </property> <!-- 指定zookeeper地址 --> <property> <name>ha.zookeeper.quorum</name> <value>hdp-05:2181,hdp-06:2181,hdp-07:2181</value> </property> </configuration>

 5.2、修改hdfs-site.xml

原来的配置,HA机制后不再需要secondaryNamenode了,需要删除该项配置。

HA机制下的配置

<configuration>
<!-- name service --> <!--指定hdfs的nameservice为bi,需要和core-site.xml中的保持一致 --> <property> <name>dfs.nameservices</name> <value>hdp24</value> </property> <!-- hdp24下面有两个NameNode,分别是nn1,nn2 --> <property> <name>dfs.ha.namenodes.hdp24</name> <!-- 这是逻辑代号,名字随便起 -->
<value>nn1,nn2</value> </property> <!-- nn1的RPC通信地址 --> <property> <name>dfs.namenode.rpc-address.hdp24.nn1</name> <value>hdp-01:9000</value> </property> <!-- nn1的http通信地址 --> <property> <name>dfs.namenode.http-address.hdp24.nn1</name> <value>hdp-01:50070</value> </property> <!-- nn2的RPC通信地址 --> <property> <name>dfs.namenode.rpc-address.hdp24.nn2</name> <value>hdp-02:9000</value> </property> <!-- nn2的http通信地址 --> <property> <name>dfs.namenode.http-address.hdp24.nn2</name> <value>hdp-02:50070</value> </property> <!-- 指定NameNode的edits元数据在机器本地磁盘的存放位置:namenode本地磁盘工作目录 --> <property> <name>dfs.namenode.name.dir</name> <value>/root/hdpdata/name</value> </property> <property>
<!-- datanode的工作目录 --> <name>dfs.datanode.data.dir</name> <value>/root/hdpdata/data</value> </property> <!-- JournalNode --> <!-- 指定NameNode的共享edits元数据在JournalNode上的存放位置,目录名最好和名称服务一致,当然这一定是一个虚拟目录 --> <property> <name>dfs.namenode.shared.edits.dir</name> <value>qjournal://hdp-05:8485;hdp-06:8485;hdp-07:8485/hdp24</value> </property> <!-- 指定JournalNode在本地磁盘存放数据的位置 --> <property> <name>dfs.journalnode.edits.dir</name> <value>/root/hdpdata/journaldata</value> </property>
<!-- zkfc --> <!-- 开启NameNode失败自动切换 --> <property> <name>dfs.ha.automatic-failover.enabled</name> <value>true</value> </property> <!-- 配置失败自动切换实现方式: 用什么控制器 --> <property>
<!-- 大型集群里可能有多对namenode,也就是多对nameservice --> <name>dfs.client.failover.proxy.provider.hdp24</name> <value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value> </property> <!-- 配置隔离机制方法,多个机制用换行分割,即每个机制暂用一行:防止脑裂的2种策略--> <property> <name>dfs.ha.fencing.methods</name> <value> sshfence
<!-- 模拟脚本 --> shell(/bin/true)
</value> </property> <!-- 使用sshfence隔离机制时需要ssh免登陆:告知秘钥位置 --> <property> <name>dfs.ha.fencing.ssh.private-key-files</name> <value>/root/.ssh/id_rsa</value> </property> <!-- 配置sshfence隔离机制超时时间,若不配置会一直发脚本,超时后就会自动调用上面配置的脚本 --> <property> <name>dfs.ha.fencing.ssh.connect-timeout</name> <value>30000</value> </property> </configuration>

5.3、修改mapred-site.xml

主要用来表示,job提交后,mapreduce运行在yarn集群上还是本地。

<configuration>
<!-- 指定mr框架为yarn方式 -->
<property>
<name>mapreduce.framework.name</name>
<value>yarn</value>
</property>
</configuration>

5.4、修改yarn-site.xml

非HA机制下的配置

HA机制下的配置。

<configuration>
<!-- 开启RM高可用 -->
<property>
<name>yarn.resourcemanager.ha.enabled</name>
<value>true</value>
</property>
<!-- 指定RM的cluster id:因为会有多个RM -->
<property>
<name>yarn.resourcemanager.cluster-id</name>
<!-- 随便起 -->
<value>yrc</value> </property> <!-- 指定RM的逻辑名字 --> <property> <name>yarn.resourcemanager.ha.rm-ids</name> <value>rm1,rm2</value> </property> <!-- 分别指定RM的地址 --> <property> <name>yarn.resourcemanager.hostname.rm1</name> <value>hdp-03</value> </property> <property> <name>yarn.resourcemanager.hostname.rm2</name> <value>hdp-04</value> </property> <!-- 指定zk集群地址:yarn集群也需要依赖Zookeeper --> <property> <name>yarn.resourcemanager.zk-address</name> <value>hdp-01:2181,hdp-02:2181,hdp-03:2181</value> </property> <property>
<!-- nodemanager 给mapreduce提供的辅助服务:mapTask把生成的结果以文件的形式写在了机器上,然后mapTask退出,reduceTask要去下载这些数据,靠nodemanager上的web服务器提供下载功能 --> <name>yarn.nodemanager.aux-services</name> <value>mapreduce_shuffle</value> </property> </configuration>

ResourceManager和nodema通信的端口号是默认配置的8032。 ResourceManager的web端口是8088

 

5.5、修改slaves

slaves其实是启动脚本需要用到的,非hadoop本身要用的。

此时有两个集群的;

  1、HDFS集群:有自己的slaves:datanode

  2、yarm集群:有自己的slaves:nodemanager,要和datanode在一块比较好

slaves是指定子节点的位置,因为要在hadoop01上启动HDFS、在hadoop03启动yarn,所以hadoop01上的slaves文件指定的是datanode的位置,hadoop03上的slaves文件指定的是nodemanager的位置

 5.6、免密钥登录

ssh-keygen -t rsa

ssh-coyp-id hadoop00
ssh-coyp-id hadoop01
ssh-coyp-id hadoop02
ssh-coyp-id hadoop03
ssh-coyp-id hadoop04
ssh-coyp-id hadoop05
ssh-coyp-id hadoop06
ssh-coyp-id hadoop07

5.7、启动

第一次启动集群

###注意:严格按照下面的步骤!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

5.7.1、启动zookeeper集群

(分别在hdp-05、hdp-06、hdp-07上启动zk)

cd /hadoop/zookeeper-3.4.5/bin/
./zkServer.sh start
#查看状态:一个leader,两个follower .
/zkServer.sh status

 5.7.2、手动启动journalnode

(分别在在hdp-05、hdp-06、hdp-07上执行)

journalnode想要工作正常必须启动Zookeeper

cd /hadoop/hadoop-2.6.4
sbin/hadoop-daemon.sh start journalnode

 

#运行jps命令检验,hadoop05、hadoop06、hadoop07上多了JournalNode进程

5.7.3、格式化namenode

因为现在namenode不仅在本地记录日志,一会在journalnode上记录日志,所以在格式化namenode之前,需要先启动journalnode;这样namenode格式化的时候会在本地以及journalnode上均写入响应的数据;

另一台namenode千万不要格式化,否则两次格式化,生成的集群id都不一样,应该是将一台格式化后的产生的文件直接拷贝到另外的一台上。保证元数据目录一模一样

#格式化后会在根据core-site.xml中的hadoop.tmp.dir配置生成一些文件夹和文件
hadoop namenode -format
hdfs namenode -format

 #格式化后会在根据core-site.xml中的hadoop.tmp.dir配置生成个文件,这里我配置的是/hadoop/hadoop-2.6.4/tmp,然后将/hadoop/hadoop-2.6.4/tmp拷贝到hadoop02的/hadoop/hadoop-2.6.4/下。

scp -r tmp/ hadoop02:/home/hadoop/app/hadoop-2.6.4/

 ##也可以这样,建议

##也可以这样,建议
hdfs namenode -bootstrapStandby

  生成的其中一个文件version

blockpoolID:将来一个大的集群(上万台)会有很多对的namenode,而datanode所有namenode公用的,namenode有很多对,datanode既要给这一对namenode存数据块,也要为另外的namenode存数据块;为了以示区分需要分目录来存取;那个目录名称就是blockpoolID

5.7.4、格式化ZKFC

(在hdp-01上执行即可)

zkfc是做状态切换的,需要在Zookeeper上记录一下信息;这就需要先创建znode节点,这就是格式化的目的。

hdfs zkfc -formatZK

 

用来记录那个namenode是active状态的。

***********************************************************************************************************************

到此为止第一次集群启动时候的初始化工作全部完成。

5.7.5、启动HDFS

(在hadoop00上执行)

这个命令会启动namenode,datanode,journalnode,zkfc

# 启动hdfs集群
sbin/start-dfs.sh

 

5.7.6、启动YARN

ResourceManager和nodema通信的端口号是默认配置的8032。 ResourceManager的web端口是8088

(#####注意#####:是在hadoop02上执行start-yarn.sh,把namenode和resourcemanager分开是因为性能问题,因为他们都要占用大量资源,所以把他们分开了,他们分开了就要分别在不同的机器上启动)

# 这个脚本只会启动本地机器上的ResourceManager和配置的slaves节点的nodemanager,另外一个RM需要手动起
sbin/start-yarn.sh

 手动启动另外一台ResourceManager

yarn-daemon.sh start resourcemanager

 

web访问standby状态的ResourceManager会充当向到active状态的ResourceManager。

5.7.7、测试集群工作状态的一些指令

查看hdfs的各节点状态信息

bin/hdfs dfsadmin -report 

 

   获取一个namenode节点的HA状态

bin/hdfs haadmin -getServiceState nn1

 

单独启动一个namenode进程

sbin/hadoop-daemon.sh start namenode 

 

 单独启动一个zkfc进程

./hadoop-daemon.sh start zkfc

 

 

5.7.8、查看Zookeeper的znode

zkfc在Zookeeper上会记录一些信息。

yarn集群的ResourceManager也会在Zookeeper记录当前active状态的rm。

 

6、hadoop的HA集群客户端编程

非HA集群下,访问HDFS的aip如下,直接访问namenode的url就可以。

 

 HA机制下,直接使用new URI("hdfs://hdp24")是无法解析的,需要在configuration中进行设置,或者将集群中的配置文件,添加到src类路径下。

从hadoopHA集群中下载配置文件。需要不集群中的配置文件放在工程src目录下。(包括core-site.xml hdfs-site.xml yarn-site.xml

在HA机制下,客户端要想知道集群中的情况,必须将集群上的配置文件放入工程的classpath中。

 

posted @ 2018-08-22 22:19  木子木泗  阅读(762)  评论(0编辑  收藏  举报