Written by 坏人,Hadoop Special Interest Group.
这学期加了hadoop源码的兴趣小组。和大家一起尝试着看hadoop中的源码。希望可以一起努力吧。
好了,介绍一下本人的情况吧。没什么hadoop的基础,也发现自己看不下hadoop权威指南这本书。感觉这本书好像介绍怎么用hadoop的内容会比较多。而自己对这个没啥兴趣,自己主要想搞清楚这个可以处理海量数据的东西是怎么运作的,运作的原理,运作的实现方式等啥的,自己可以知道里面的一些东西,自己就满足了。实践啥的,现在没有啥硬件条件,所以就先弄懂他的原理呗。不过说起来hadoop中的很多东西其实都不是什么新东西,如里面的抽象的文件系统,里面的mapreduce过程感觉又和多处理器的一些原理相似,只不过抽象的层次不同。他的价值在更多的在于对大数据量的处理。自己的兴趣也在这一块,所以,你懂得。
好了,说了那么多,开始对hadoop源码的第一部分:hdfs的解读吧。
说起hdfs,就会提到这张比较经典HDFS文件写入过程(在《hadoop权威指南》中文版的71页,英文版的66页),它介绍了文件写入的大致过程(这个请读者自己看一下这个过程,我会当你知道这个大概的过程):
我们可以暂且将往HDFS里面写文件的过程暂且看成三个部分:namenode、datanode和DFSClient。我先从namenode入手看,各个击破吧。
从老外的注释中,我们可以看到,namenode主要保存了两种信息:
1) filename->blocksequence (namespace)
2) block->machinelist ("inodes")
但是这两部分主要都是由FSNamesystem完成,Namenode自己主要实现了三个通信接口:
-
ClientProtocol:和客户端的通信
-
DatanodeProtocol:和datanode的通信
-
NamenodeProtocol:主要和second namenode的通信
在实际的代码中,namenode还实现了两个接口:RefreshAuthorizationPolicyProtocol以及 RefreshUserMappingsProtocol,这两个IPC协议从名字上看应该是和用户认证有关系的。(PS:其实自己也很好奇,hadoop里面好像没啥用用户名和密码登录的过程,其对文件的保护好像就是根据文件的保护权限来限制,那个hadoop怎么在客户端连接过来的时候知道他是那个用户呢,怎么保证他就是那个用户呢,这个应该就和这几个IPC有关吧,仅猜测,接下来看到相应的源码就知道了)在实际的代码中,都会用到这几个协议,这几个协议交互的内容和交互的过程也是接下来的工作之一了。
好了,让我们来看看FSNamenode是怎么实现将文件名对应到相应的几台datanode的过程了(上面两个过程的整合)。
先列出来FSNamesytem中的主要的几个元素,:
1、FSDirectory dir;
2、BlockMap blocksMap;
3、CorruptReplicasMap corruptReplicas;
4、PendingReplicationBlocks pendingReplications
5、UnderReplicatedBlocks neededReplications;
6、TreeMap<String,Collection<Block>> resentInvalidateSets;
7、TreeMap<String,Collection<Block>> excessReplicateMap;
8、LeaseManager leaseManager;
9、TreeMap<String,DatanodeDescriptor> datanodeMap;
接下来的讲解我根据了另外一篇讲的不错的文章的结构来讲解,文章的最后我会附上参考网文的网址。
首先看看filename--->block的实现。在运行时,这个对应关系一直放在内存中,也会放在硬盘中(fsimage文件和相应的edits.log文件)。
在内存中,FSNamesystem靠保存一棵目录树维持文件的目录结构,用到如下几个数据结构(如下图),这几个结构都和linux的文件系统类似,INode作为基类保存文件或者目录的名称,INodeDirectory保存一个INode列表,表示目录下的文件或者目录,INodeFile保存文件的信息,如block号,文件权限等。这个没什么好详细介绍的。FSNamesystem中的dir会保存一个INodeDirectoryWithQuota类型的rootDir保存根节点。
在硬盘中,目录的信息以fsimage文件的形式存放,并将读入fsimage之后对文件结构的操作放在edits.log中。启动时,FSDirectory调用FSImage的LoadFSImage方法和LoadFSEdits方法将fsimage和edits.log读入内存,合成为新的目录结构。Secondnamenode会定期的读取namenode中的这两个文件,合成为一个新的fsimage文件,更新namenode中的fsimage(等一下会大概讲一下这个过程)。
硬盘上的fsimage的结构在网上盛传着一张“淘宝师兄画的图”。没找到出处,先直接拿过来借用一下。
-
imgVersion:image版本号
-
namespaceID:命名空间ID,生命周期内不会变。主要是要和datanode注册时使用的registrationID相同就行了。
-
numFiles:文件和目录的数目,你懂得。
-
genStamp:时间戳,暂时还不知道为啥要生成这个时间戳。
-
Path:你懂得,文件或者目录的路径名
-
replicas:记录namenode知道这个文件的副本数,这个参数对目录没用(目录为0)。
-
mtime:make time
-
atime:access time
-
blocksize:目录的该参数为0,记录block大小
-
numBlock:block数目,目录此参数为-1
-
nsQuota和dsQuota:这两个参数默认为-1,暂时还不知道这两个参数的含义。
-
username、group和perm:记录文件的用户名,组名,权限
如果是文件的话,后面会有numBlock个<blockid,numbytes,genStamp>数据。
再说说block--->datanode的关系,它通过BlocksMap类型的blocksMap元素保存在内存中。不在硬盘保存。它通过datanode的blockReport来收集构建。让datanode来告诉namenode哪一个datanode有什么block。
BlocksMap中有一个GSet<Block,BlockInfo>的元素,这涉及到两个结构,Block和BlockInfo,我将这两个结构中比较重要的信息画出来,如下图:
这里面BlockInfo有一个比较重要的结构,就是Object[3] triplets。因为一个block可能在多个datanode中都有备份,有多少个备份就会有多少个BlockInfo,这些BlockInfo之间通过Object[3]这个结构中的第二和第三个元素串成一个双向链表的结构,而Block只需要对应第一个BlockInfo即可实现一对多的关系。(好像讲的有点乱。。。大家凑活理解吧)
说到这里,好像Namenode交给FSNamesystem该完成的任务已经靠前面结构中的dir和blocksMap这两个元素完成了。那还要其他元素干什么呢?
好吧,一开始我也是这么想的。但是一个完整的文件系统需要处理出错,失效的block。同时还要处理一些对文件的锁机制(如防止两个用户对同一个文件的同时写入)等,还要记录datanode的一些信息(说了,namenode比较懒)。这些靠上面贴出来的剩下的一些元素完成。鉴于这篇文章已经有点长了,所以我另起一篇。来继续介绍剩余的元素。