理解FSImage,首先需要理解INode。
下面是Hadoop2.6.0下的INode类图。
INode的源码如下
public abstract class
INode
implements
INodeAttributes
,
Diff.Element<
byte
[]>
,
AuthorizationProvider.INodeAuthorizationInfo {
private INode
parent
= null;
final boolean
isRoot
() {
return getLocalNameBytes().
length
==
0
;
}
/**
* Information used for updating the blocksMap when deleting files.
*/
public static class
BlocksMapUpdateInfo {
/**
* The list of blocks that need to be removed from blocksMap
*/
private final
List
toDeleteList
;
...
}
/**
* INode feature such as {
@link
FileUnderConstructionFeature}
* and {
@link
DirectoryWithQuotaFeature}.
*/
public interface
Feature {
}
}
属性就一个
parent,
它的作用是在快照当中进行版本判断。其余都是与
INode
权限相关方法。最终要的是有一个内部类
BlocksMapUpdateInfo
用来在删除文件的时候更新
blockMap
。还有一个
Feature
接口,这个
Feature
有多个具体实现如下:
可以看出来Feature具体类是用来表现
INode
的特性的,
AclFeature
是用来表示ACL的
DirectoryWithQuotaFeature
是用来记录配额信息的,
FileUnderConstructionFeature
使用来记录在读取文件的信息的,
XAttrFeature
是用来保存额外信息的,下面三个都是和快照实现相关的特性类。
我们可以看到
Feature
是作为
INodeWithAdditionalFields
的具体属性的,这个类做为一个抽象类,在INode基础上增加了作为文件、目录或者软连接所必要的几个信息,具体为
final private long
id
;
private byte
[]
name
=
null;
private long
permission
=
0L
;
private long
modificationTime
=
0L
;
private long
accessTime
=
0L
;
protected
Feature[]
features
=
EMPTY_FEATURE
;
其中大部分属性看名字就知道作用,唯独
permission
具体说明下:
首先看下
INodeWithAdditionalFields
的源码
static enum
PermissionStatusFormat {
MODE
(
null,
16
)
,
GROUP
(
MODE
.
BITS
,
25
)
,
USER
(
GROUP
.
BITS
,
23
)
;
final
LongBitFormat
BITS
;
static
String
getUser
(
long
permission) {
final int
n = (
int
)
USER
.
BITS
.retrieve(permission)
;
return
SerialNumberManager.
INSTANCE
.getUser(n)
;
}
...
}
已及相关类
LongBitFormat
的源码:
public class
LongBitFormat
implements
Serializable {
private static final long
serialVersionUID
=
1L
;
private final
String
NAME
;
/** Bit offset */
private final int
OFFSET
;
/** Bit length */
private final int
LENGTH
;
/** Minimum value */
private final long
MIN
;
/** Maximum value */
private final long
MAX
;
/** Bit mask */
private final long
MASK
;
}
结合来看很容易知道
permission
用一个
long
型保存了
MODE
,
GROUP
,
USER
三个具体内容。
此外
INodeWithAdditionalFields
抽象类,在
INode
基础上对很多抽象方法进行了实现,主要还是权限相关和特性
Feature
及作为文件、目录软连接用的基础方法。
INodeFile类在
INodeWithAdditionalFields
基础上多了
header
属性和
blocks
属性,其中
header
同父类
INodeWithAdditionalFields
的
permission
一样也是用一个
long
类型记录多个属性值,具体为与文件相关的
PREFERRED_BLOCK_SIZE
(
null,
48
,
1
)
,
REPLICATION
(
PREFERRED_BLOCK_SIZE
.
BITS
,
12
,
1
)
,
STORAGE_POLICY_ID
(
REPLICATION
.
BITS
,
BlockStoragePolicySuite.
ID_BIT_LENGTH
,
0
)
;
相信看过Hadoop的一眼就能知道这些代表什么。
blocks
记录的是
INodeFile
和文件块之间的关系(Hadoop中的第一类关系),同时INodeFile类提供了block处理相关的几个方法,因为与fsImage相关性不大,这里就不分析了。
INodeDirectory
类相较于
INodeWithAdditionalFields
只多了一个属性
children
,用来记录层级关系,这样就构成了树状结构。文件夹相关的配额信息等都通过提供的方法写入了
INodeWithAdditionalFields
的
Feature上
了。
INodeSymlink
也只是比
INodeWithAdditionalFields
多了一个属性
symlink
用来标记软连接的目标。
INodeReference是一个抽象类,它拓展自INode类,所以INodeReference及其子类是可以添加到文件系统目录树中以替代原有的INodeFile节点的。INodeReference定义了referred字段,这个字段用于保存当前INodeReference类指向的INode节点,所以WithName和RstReference,referred字段就指向了WithCount对象,对于WithCount,referred指向了真正的INode对象。INodeReference还定义了getReferredINode()方法,在文件系统目录树的操作中,如果判断当前节点是一个引用节点,则会调用getReferredINode()方法获取INodeReference指向的INode对象。
具体源码如下
public
abstract
class
INodeReference
extends
INode {
private
INode referred;
//指向的INode节点
public
INodeReference(INode parent,INode referred){//这里用到了INode的字段parent
super
(parent);
this
.referred = referred;
}
//...
}
然后,我们在来看看WithCount类的实现。
WithCount类定义了一个集合字段withNameList用于保存所有指向这个WithCount对象的WithName对象集合。WithCount类还定义了addReference()方法,任何指向WithCount对象的WithName对象以及DstReference对象都需要调用这个方法来添加指向关系。对于指向这个WithCount对象的DstReference对象,addReference()方法会将这个对象设置为自己的父INode节点;而对于WithName对象,addReference()方法则将这个对象放入withNameList集合中保存。
public static class WithCount extends INodeReference {
//保存所有指向这个WithCount对象的WithName对象的集合
private
final
List withNameList =
new
ArrayList();
public
WithCount(INodeReference parent,INode referred) {
super
(parent,referred);
//调用父类的构造方法,指向文件系统目录树中的INode
Preconditions.checkArgument(!referred.isReference());
refferred.setParentReferenct(
this
);
//设置真实INode的父节点为当前WithCount对象
}
public
void
addReferenct(INodeReference ref){
if
( ref
instanceof
WithName) {
//如果是WithName对象,则加入withNameList
WithName refWithName = (WithName) ref;
in
i = Collections.binarySearch(withNameList, refWithName,WITHNAME_COMPARATOR);
Preconditions.checkState(i<
0
);
withNameList.add(-i-
1
,refWithName);
}
else
if
(ref
instanceo
DstReference) {
//如果是DstReference对象,则设置为父节点
setParentReference(ref);
}
}
//...
}
看完WithCount后,在看看WithName和DstReference。WithName类定义了name字段用于保存重命名前文件的名称,同事定义了lastSnapshotId字段用于保存WithName对象构造时源路径的快照版本号。DstReference类的实现就更简单了,只定义了一个dstSnapshotId字段用于保存重命名操作前目标路径的最新快照的版本号。WithName和DstReference在构造时都会调用父类的构造方法指向WithCount对象,同时还会调用WithCount.addReference()方法配置WithCount对象。
public static class WithName extend INodeReference {
private
final
byte
[] name;
//重命名前的文件名
private
final
int
lastSnapshotId;
public
WithName(INodeDirectory parent,WithCount referred,bytep[] name,
in
lastSnapshotId){
super
(parent,referred);
//调用父类构造方法,指向WithCount节点
this
.name = name;
this
.lastSnapshotId = lastSnapshotId;
referred.addReferenct(
this
);
//调用WithCount.addReferenct()
}
//...
}
public
static
class
DstReference
extends
INodeReference {
private
final
int
dstSnapshotId;
public
DstReference (INodeDirectory parent,WithCount referred,
fina
int
dstSnapshotId){
super
(parent,referred);
this
.lastSnapshotId = lastSnapshotId;
referred.addReferenct(
this
);
//调用WithCount.addReferenct()
}
//..
}
快照相关类的解析参考自 http://blog.csdn.net/learnboc/article/details/70258541
看完了INode的类之间的关系及各自的作用那么理解FSImage的结构就容易了。首先上图
将内存中INode的相关信息持久化FSImage文件上。
FSImage最开始的8个字节为
MAGIC_HEADER
(
value=“HDFSIMG1
”)
中间为10个sections (通过
FileSummary
.parseDelimitedFrom(
new
ByteArrayInputStream(summaryBytes))
;
得到FileSummary
)
分别为:{
0:name:”NS_INFO” 记录HDFS文件系统的全局信息,包括NameSystem的ID,当前已经分配出去的最大BlockID以及TransactionId等信息;具体字段
1:name: “INODE" 整个目录树所有节点数据,包括INodeFile/INodeDirectory/INodeSymlink等所有类型节点的属性数据,其中记录了如节点id,节点名称,访问权限,创建和访问时间等等信息;
2:name: “INODE_DIR" 整个目录树中所有节点之间的父子关系,配合INODE可构建完整的目录树;
3:name: “FILES_UNDERCONSTRUCTION" 尚未完成写入的文件集合,主要为重启时重建Lease集合;
4:name: “SNAPSHOT" 记录Snapshot数据,快照是Hadoop 2.1.0引入的新特性,用于数据备份、回滚,以防止因用户误操作导致集群出现数据问题;
5:name: “SNAPSHOT_DIFF" 执行快照操作的目录/文件的Diff集合数据,与SNAPSHOT一起构建较完整的快照管理能力;
6:name: “INODE_REFERENCE" 当目录/文件被操作处于快照,且该目录/文件被重命名后,会存在多条访问路径,INodeReference就是为了解决该问题;
7:name: “SECRET_MANAGER" 记录DelegationKey和DelegationToken数据,根据DelegationKey及由DelegationToken构造出的DelegationTokenIdentifier方便进一步计算密码,以上数据可以完善所有合法Token集合;
8:name: “CACHE_MANAGER" 集中式缓存特性全局信息,集中式缓存特性是Hadoop-2.3.0为提升数据读性能引入的新特性;
9:name: “STRING_TABLE" 字符串到id的映射表,维护目录/文件的Permission字符到ID的映射,节省存储空间;
接下去是summary区域,它主要标识了上面10个section区域各区域的name和在fsimage上的起始位置offset及各区域所占的长度length,此外还记录了fsimage的版本号ondiskVersion布局版本号layoutVersion及解压/压缩器codec
文件最后(占4个字节)记录的是summary信息所占空间的长度信息(通过file.seek(fileLength - FILE_LENGTH_FIELD_SIZE)获得)
;
)。