/**********************************************************
* NameNode servesas both directory namespace manager and
* "inodetable" for the Hadoop DFS. There isa single NameNode
* running in anyDFS deployment. (Well, except when there
* is a secondbackup/failover NameNode.)
*
* The NameNodecontrols two critical tables:
* 1) filename->blocksequence (namespace)
* 2) block->machinelist ("inodes")
*
* The first tableis stored on disk and is very precious.
* The second tableis rebuilt every time the NameNode comes
* up.
*
* 'NameNode'refers to both this class as well as the 'NameNode server'.
* The'FSNamesystem' class actually performs most of the filesystem
* management. The majority of the 'NameNode' class itselfis concerned
* with exposingthe IPC interface and the http server to the outside world,
* plus someconfiguration management.
*
* NameNodeimplements the ClientProtocol interface, which allows
* clients to askfor DFS services. ClientProtocol is not
* designed fordirect use by authors of DFS client code. End-users
* should insteaduse the org.apache.nutch.hadoop.fs.FileSystem class.
*
* NameNode alsoimplements the DatanodeProtocol interface, used by
* DataNodeprograms that actually store DFS data blocks. These
* methods areinvoked repeatedly and automatically by all the
* DataNodes in aDFS deployment.
*
* NameNode alsoimplements the NamenodeProtocol interface, used by
* secondarynamenodes or rebalancing processes to get partial namenode's
* state, forexample partial blocksMap etc.
**********************************************************/
对于HDFS来说NameNode是一个目录命名空间管理器和”inode table”,它是一个单个的NameNode运行在任何的DFS的部署环境中(好吧,除非是有第二个备份/故障转移NameNode。)
NameNode控制着两个关键表:
1) filename->blocksequence (namespace)第一个表名为文件名,放的是block的顺序
2) block->machinelist ("inodes")第二个表是block,放的是block所存放的机器列表
第一个表存放在硬盘上并且非常珍贵
第二个表在每次NameNode重启的时候会被重构
NameNode指的是这个类以及NameNode server,FSNamesystem这个类实际上执行的大多数文件系统管理。NameNode这个类主要的工作就是关注暴露的IPC接口以及向外部世界(用户)提供一些http服务,加上一些配置管理。
NameNode实现了ClientProtocol接口,它允许客户请求DFS服务。ClientProtocol不是专门为直接使用DFS客户机代码的作者设计的,终端用户(程序员)应该使用FileSystem这个类。
NameNode也实现了DatanodeProtocol接口,被DataNode的程序使用去完成DFS数据块的存储。在一个DFS的环境中NameNode实现了DatanodeProtocol接口中的方法会被所有的DataNode自动重复的调用执行。
NameNode也实现了NamenodeProtocol接口,被secondarynamenode使用或在平衡过程的进程中得到NameNode 的部分状态,例如部分blocksMap等等
# 首先看namenode类的结构,主要实现了ClientProtocol, DatanodeProtocol, NamenodeProtocol这三个接口
# 进入NameNode的源代码找到publicclass NameNodeimplements ClientProtocol, DatanodeProtocol, NamenodeProtocol,FSConstants,RefreshAuthorizationPolicyProtocol,
RefreshUserMappingsProtocol {
# 接下来进入main方法(由于NameNode是一个RPC的服务端,所以我们进入RPC的main方法,为了证明NameNode是一个RPC的服务端)
public static void main(String argv[]) throws Exception {
try {
StringUtils.startupShutdownMessage(NameNode.class,argv,LOG);
NameNode namenode = createNameNode(argv,null);
if (namenode != null)
namenode.join();
} catch (Throwable e) {
LOG.error(StringUtils.stringifyException(e));
System.exit(-1);
}
}
# 进入createNameNode方法(只看重点,会有下面这么一行)
NameNodenamenode = new NameNode(conf);
# 再点进去
public NameNode(Configuration conf) throws IOException {
try {
initialize(conf);
} catch (IOException e) {
this.stop();
throw e;
}
}
# 进入initialize(conf)方法(只看重点代码)
this.namesystem =newFSNamesystem(this, conf);
if (UserGroupInformation.isSecurityEnabled()){
namesystem.activateSecretManager();
}
// create rpc server
InetSocketAddress dnSocketAddr =getServiceRpcServerAddress(conf);
if (dnSocketAddr !=null) {
int serviceHandlerCount =
conf.getInt(DFSConfigKeys.DFS_NAMENODE_SERVICE_HANDLER_COUNT_KEY,
DFSConfigKeys.DFS_NAMENODE_SERVICE_HANDLER_COUNT_DEFAULT);
this.serviceRpcServer = RPC.getServer(this, dnSocketAddr.getHostName(),
dnSocketAddr.getPort(),serviceHandlerCount,
false, conf,namesystem.getDelegationTokenSecretManager());
this.serviceRPCAddress =this.serviceRpcServer.getListenerAddress();
setRpcServiceServerAddress(conf);
}
this.server = RPC.getServer(this, socAddr.getHostName(),
socAddr.getPort(),handlerCount, false, conf,namesystem
.getDelegationTokenSecretManager());
// The rpc-server port can be ephemeral... ensure we have the correct info
this.serverAddress =this.server.getListenerAddress();
FileSystem.setDefaultUri(conf,getUri(serverAddress));
LOG.info("Namenode up at: " +this.serverAddress);
startHttpServer(conf);
this.server.start(); //start RPC server
if (serviceRpcServer !=null) {
serviceRpcServer.start();
}
startTrashEmptier(conf);
namesystem后面再解释(namenode的初始化,namenode启动加载fsimage以及一些配置,后面详细解释)
//create rpc server 意思就是创建 RPC服务端,也就是说NameNode是一个RPC的服务端
注意:这里不是启动了一个rpc的服务端,而是启动了两个rpc的服务端。
serviceRpcServer:RPC服务器为了HDFS服务通信。备份节点(secondarynamenode),Datanodes和所有其他服务应该连接到这个服务器配置。客户应该只去调用NameNode 下的server的RPC服务端(这个是程序内部调用的)
server:主要是用来给客户端调用的
# 然后再来看startHttpServer(conf);(开启一个Http的服务器)这个方法,跟进去
try {
this.httpServer = ugi.doAs(new PrivilegedExceptionAction
@Override
public HttpServer run()throws IOException,InterruptedException {
String infoHost =infoSocAddr.getHostName();
int infoPort = infoSocAddr.getPort();
httpServer = new HttpServer("hdfs", infoHost,infoPort,
infoPort == 0, conf,
SecurityUtil.getAdmin
# 在进入HttpServer 的构造方法,跟进new HttpServer("hdfs", infoHost, inf
public HttpServer(String name, String bindAddress, int port,
boolean findPort, Configuration conf,AccessControlList adminsAcl)
throws IOException {
this(name, bindAddress, port, findPort, conf,adminsAcl,null);
}
# 再跟进this(name, bindAddress, port, findPort, conf,adminsAcl,null);
public HttpServer(String name, String bindAddress, int port,
boolean findPort, Configuration conf,AccessControlList adminsAcl,
Connector connector) throws IOException{
webServer =new Server();
this.findPort = findPort;
this.conf = conf;
this.adminsAcl = adminsAcl;
if(connector ==null) {
到这里就行了,把鼠标放到new Server()上面去,可以看到这么一行提示信息
org.mortbay.jetty.Server.Server()
这里额外说明一下jetty,jetty跟tomcat一样,也是一个服务器,只是更小而已,被内置到NameNode中去了。
到了这里之后就可以看出来NameNode开启了一个jetty服务,也就是可以通过浏览器访问,也就是我们经常访问的http://hadoopip:50070(这里hadoopip表示你的hadoop机器的ip地址)出现的web界面
Namenode会将HDFS的文件和目录元数据存储在一个叫fsimage的二进制文件中,每次保存fsimage之后到下次保存之间的所有hdfs操作,将会记录在editlog文件中,当editlog达到一定的大小(bytes,由fs.checkpoint.size参数定义)或从上次保存过后一定时间段过后(sec,由fs.checkpoint.period参数定义),namenode会重新将内存中对整个HDFS的目录树和文件元数据刷到fsimage文件中。Namenode就是通过这种方式来保证HDFS中元数据信息的安全性。
Fsimage是一个二进制文件,当中记录了HDFS中所有文件和目录的元数据信息,在我的hadoop的HDFS版中,该文件的中保存文件和目录的格式如下:
当namenode重启加载fsimage时,就是按照如下格式协议从文件流中加载元数据信息。从fsimag的存储格式可以看出,fsimage保存有如下信息:
1. 首先是一个image head,其中包含:
a) imgVersion(int):当前image的版本信息
b) namespaceID(int):用来确保别的HDFSinstance中的datanode不会误连上当前NN。
c) numFiles(long):整个文件系统中包含有多少文件和目录
d) genStamp(long):生成该image时的时间戳信息。
2. 接下来便是对每个文件或目录的源数据信息,如果是目录,则包含以下信息:
a) path(String):该目录的路径,如”/user/build/build-index”
b) replications(short):副本数(目录虽然没有副本,但这里记录的目录副本数也为3)
c) mtime(long):该目录的修改时间的时间戳信息
d) atime(long):该目录的访问时间的时间戳信息
e) blocksize(long):目录的blocksize都为0
f) numBlocks(int):实际有多少个文件块,目录的该值都为-1,表示该item为目录
g) nsQuota(long):namespaceQuota值,若没加Quota限制则为-1
h) dsQuota(long):disk Quota值,若没加限制则也为-1
i) username(String):该目录的所属用户名
j) group(String):该目录的所属组
k) permission(short):该目录的permission信息,如644等,有一个short来记录。
3. 若从fsimage中读到的item是一个文件,则还会额外包含如下信息:
a) blockid(long):属于该文件的block的blockid,
b) numBytes(long):该block的大小
c) genStamp(long):该block的时间戳
当该文件对应的numBlocks数不为1,而是大于1时,表示该文件对应有多个block信息,此时紧接在该fsimage之后的就会有多个blockid,numBytes和genStamp信息。
因此,在namenode启动时,就需要对fsimage按照如下格式进行顺序的加载,以将fsimage中记录的HDFS元数据信息加载到内存中。
从以上fsimage中加载如namenode内存中的信息中可以很明显的看出,在fsimage中,并没有记录每一个block对应到哪几个datanodes的对应表信息,而只是存储了所有的关于namespace的相关信息。而真正每个block对应到datanodes列表的信息在hadoop中并没有进行持久化存储,而是在所有datanode启动时,每个datanode对本地磁盘进行扫描,将本datanode上保存的block信息汇报给namenode,namenode在接收到每个datanode的块信息汇报后,将接收到的块信息,以及其所在的datanode信息等保存在内存中。HDFS就是通过这种块信息汇报的方式来完成 block-> datanodes list的对应表构建。Datanode向namenode汇报块信息的过程叫做blockReport,而namenode将block -> datanodeslist的对应表信息保存在一个叫BlocksMap的数据结构中。
BlocksMap的内部数据结构如下:
如上图显示,BlocksMap实际上就是一个Block对象对BlockInfo对象的一个Map表,其中Block对象中只记录了blockid,block大小以及时间戳信息,这些信息在fsimage中都有记录。而BlockInfo是从Block对象继承而来,因此除了Block对象中保存的信息外,还包括代表该block所属的HDFS文件的INodeFile对象引用以及该block所属datanodes列表的信息(即上图中的DN1,DN2,DN3,该数据结构会在下文详述)。
因此在namenode启动并加载fsimage完成之后,实际上BlocksMap中的key,也就是Block对象都已经加载到BlocksMap中,每个key对应的value(BlockInfo)中,除了表示其所属的datanodes列表的数组为空外,其他信息也都已经成功加载。所以可以说:fsimage加载完毕后,BlocksMap中仅缺少每个块对应到其所属的datanodeslist的对应关系信息。所缺这些信息,就是通过上文提到的从各datanode接收blockReport来构建。当所有的datanode汇报给namenode的blockReport处理完毕后,BlocksMap整个结构也就构建完成。
在BlockInfo中,将该block所属的datanodes列表保存在一个Object[]数组中,但该数组不仅仅保存了datanodes列表,“。实际上该数组保存了如下信息:
上图表示一个block包含有三个副本,分别放置在DN1,DN2和DN3三个datanode上,每个datanode对应一个三元组,该三元组中的第二个元素,即上图中prev block所指的是该block在该datanode上的前一个BlockInfo引用。第三个元素,也就是上图中next Block所指的是该block在该datanode上的下一个BlockInfo引用。每个block有多少个副本,其对应的BlockInfo对象中就会有多少个这种三元组。
Namenode采用这种结构来保存block->datanodelist的目的在于节约namenode内存。由于namenode将block->datanodes的对应关系保存在了内存当中,随着HDFS中文件数的增加,block数也会相应的增加,namenode为了保存block->datanodes的信息已经耗费了相当多的内存,如果还像这种方式一样的保存datanode->blocklist的对应表,势必耗费更多的内存,而且在实际应用中,要查一个datanode上保存的block list的应用实际上非常的少,大部分情况下是要根据block来查datanode列表,所以namenode中通过上图的方式来保存block->datanode list的对应关系,当需要查询datanode->blocklist的对应关系时,只需要沿着该数据结构中next Block的指向关系,就能得出结果,而又无需保存datanode->blocklist在内存中。
Fsimage加载过程完成的操作主要是为了:
1. 从fsimage中读取该HDFS中保存的每一个目录和每一个文件
2. 初始化每个目录和文件的元数据信息
3. 根据目录和文件的路径,构造出整个namespace在内存中的镜像
4. 如果是文件,则读取出该文件包含的所有blockid,并插入到BlocksMap中。
整个加载流程如下图所示:
如上图所示,namenode在加载fsimage过程其实非常简单,就是从fsimage中不停的顺序读取文件和目录的元数据信息,并在内存中构建整个namespace,同时将每个文件对应的blockid保存入BlocksMap中,此时BlocksMap中每个block对应的datanodes列表暂时为空。当fsimage加载完毕后,整个HDFS的目录结构在内存中就已经初始化完毕,所缺的就是每个文件对应的block对应的datanode列表信息。这些信息需要从datanode的RPC远程调用blockReport中获取,所以加载fsimage完毕后,namenode进程进入rpc等待状态,等待所有的datanodes发送blockReports。
每个datanode在启动时都会扫描其机器上对应保存hdfs block的目录下(dfs.data.dir)所保存的所有文件块,然后通过namenode的rpc调用将这些block信息以一个long数组的方式发送给namenode,namenode在接收到一个datanode的blockReport rpc调用后,从rpc中解析出block数组,并将这些接收到的blocks插入到BlocksMap表中,由于此时BlocksMap缺少的仅仅是每个block对应的datanode信息,而namenoe能从report中获知当前report上来的是哪个datanode的块信息,所以,blockReport过程实际上就是namenode在接收到块信息汇报后,填充BlocksMap中每个block对应的datanodes列表的三元组信息的过程。其流程如下图所示:
当所有的datanode汇报完block,namenode针对每个datanode的汇报进行过处理后,namenode的启动过程到此结束。此时BlocksMap中block->datanodes的对应关系已经初始化完毕。如果此时已经达到安全模式的推出阈值,则hdfs主动退出安全模式,开始提供服务。
一个hdfs的cluster包含了一个NameNode和若干个DataNode,NameNode是master,主要负责管理hdfs文件系统,具体的包括namespace管理(目录结构)和block管理(具体filename->blocksequence(namespace),block->datanode list(“inodes”))。前者是通过FSImage写入到本地文件系统中,而后者是通过每次hdfs启动时,datanode进行blockreport后在内存中重构的数据结构。在hdfs的程序代码中,namenode类其实只是一个用来被动接收调用的服务的包装,它实现了ClientProtocol接口,用来接收来自DFSClient的RPC请求;它实现了DatanodeProtocol接口,用来接收来自datanode的各种服务请求;同时还实现了NamenodeProtocol,用来提供跟SeconddaryNameNode之间的RPC的请求和通信。对以上数据结构进行维护的是hdfs中的FSNamesystem类。对于NameNode的各种请求,比如创建,修改,删除,移动,getLocations的操作,在NameNode内部都是通过FSNamesystem提供的接口对内部数据结构进行的访问。
NameNode是一个目录命名空间的管理器,NameNode在hdfs中只有一个。(当启动一个NameNode的时候,会产生一个锁文件,是锁住的,所以起不了第二个NameNode了)
NameNode维护这两张核心表:
1. Filename------blocksequence (“namespace”)也就是block的顺序
2. block------machinelist(“inodes”) 每个block的存储的机器(dataNode)列表
NameNode其实就是一个RPC的服务端,并且启动了两个RPC服务端(这里又涉及到了RPC原理了,看不懂的话就看下RPC的原理),并且还开启了一个jetty服务器,对外界提供了WEB的访问方式。
妳那伊抹微笑
The you smile until forever 、、、、、、、、、、、、、、、、、、、、、