1.概述
在glusterfs中,文件的定位采用弹性hash算法进行定位。集群中的任何服务器和
客户端只需根据路径和文件名就可以对数据进行定位和读写访问。换句话说,GlusterFS不需要将元数据与数据进行分离,因为文件定位可独立并行化进行。GlusterFS中数据访问流程如下:
1)计算hash值,输入参数为文件路径和文件名;
2)根据hash值在集群中选择子卷(存储服务器),进行文件定
3)对所选择的子卷进行数据访问。
GlusterFS目前使用Davies-Meyer算法计算文件名hash值,获得一个32位整数。Davies-Meyer算法具有非常好的hash分布性,计算效率很高。假设逻辑卷中的存储服务器有N个,则32位整数空间被平均划分为N个连续子空间,每个空间分别映射到一个存储服务器。这样,计算得到的32位hash值就会被投射到一个存储服务器,即我们要选择的子卷。后面我们会对这部分有详细的分析。
在具体代码实现中,Dht为glusterfshash算法实现部分,处于gluster客户端xlator树的中间层,整个文件系统hash算法均由该部分负责,该部分具体处的位置如下图所示:
dht在整个gluster系统中的位置示意图
其在客户端卷配置文件如下:
15 volume v1-dht
16 type cluster/distribute
17 subvolumes v1-client-0 v1-client-1
18 end-volume
该部分目录树形结构如下:
Dht目录树形结构图
文件功能说明:
dht.c:分布式hash算法的主文件,在它内部包含了dht xlator的初始化,析构,对文件的操作定义,xlator参数的合法性验证,事件通知等;
dht-common.c:实现了dht.c中定义的所有dht相关的文件操作;
dht-common.h: dht-common.c的头文件,包含了dht部分的结构体定义,和一些函数的声明;
dht-diskusage.c:dht下所有的子卷相关的存储节点的存储空间使用情况收集;判断某个卷磁盘空间是否已经被塞满;找出可用磁盘空间最多的卷;
dht-hashfn.c:通过计算获得hash值
Dht部分具体代码分析
在gluster中,xlator的设计还是很清晰的,dht作为xlator的一员,依然继承了xlator的实现风格,我们结合类图首先整体认识dht:
dht整体类图:
类图说明:
init函数:用于dht的初始化,在客户端挂载服务启动的时候,客户端会解析xlator树,调用每一个节点的init函数进行xlator的初始化,这个时候dht也被初始化;
fini析构函数:在解析客户端xlator树时被调用将其从内存中清除;
options结构体:用于其参数声明;
fops结构体:dht最核心的部分,其所有动作如如何进行文件的创建,读写等都在其内部进行定义,并且在dht-common.c实现;当它的父节点调用它时,将会调用这些操作完成它应该完成的功能,包含最重要的分布式hash
notify函数:dht的事件通知函数
6)reconfigure函数:对其部分配置参数进行重新配置;
注:上面的所有函数和结构体在所有的xlator都有(个别有例外)
2.功能验证
Hash下的文件夹xattr信息:
>>> xattr.listxattr("renqiang") (u'security.selinux', u'trusted.gfid', u'trusted.glusterfs.dht') >>> xattr.getxattr("renqiang","trusted.glusterfs.dht") '\x00\x00\x00\x01\x00\x00\x00\x00?\xff\xff\xff\x7f\xff\xff\xfd' |
Hash下文件的xattr信息:
>>> xattr.listxattr("7") (u'security.selinux', u'trusted.gfid') >>> xattr.getxattr("7","trusted.gfid") '-\x9c;{\xfb\xd6F\xa3\xa4\xe4\xaf\xf1\x08\xc8\x01\x1d' |
下面开始分析重点函数和结构体部分流程(具体结构体的内部参数说明请看gluster源码研究之基础数据结构部分)
Init函数处理流程
dht的初始化过程,实际上就是对它已经在配置文件中赋值过了的参数进行合法性检查,和在配置文件中不能赋初值,或者没有赋初值的一些参数赋初值的过程。其初始化流程可以表示成这样:
init执行流程图
说明:
父子节点检查主要是检查是否有至少2个子节点,比存存在父节点;
结构体对象在使用前都需进行内存分配;
Conf为结构体dht_conf_t的对象,里面维护了xlator需要的很多基础参数;
Xlator操作过程中会涉及到很多加锁解锁的地方,在此对锁进行初始化,则之后需要用锁的地方可以直接加锁解锁了;
当conf参数都赋值完后,要将其赋值给xlator的private参数;
初始化过程中如果遇到异常等将直接转入错误处理部分,即释放掉初始化过程中分配的内存,初始化失败;
fini执行流程
该过程就是将dhtxlator占用的内存资源释放掉,由于该部分涉及到内容比较简单,在次不再赘诉,可以去看代码了解
fop部分
该部分包含了所有dht涉及到的操作。操作分为2类:1类是结合hash算法来实现的操作;2类是间接利用了hash的结果来实现的操作。
为什么会分为2类呢?当文件的创建,通过hash,磁盘空闲空间等可以确定文件将创建哪一个子卷对应的存储节点上,同时将选择的子卷相对应的信息用dht的相应参数进行了记录;当进行文件的写,读,扩展数据的操作等时,可以直接从记录的子卷信息中获得相应的信息,然后获得相应的文件,图示如下:
hash算法示意图
2.功能验证
2.1.重命名
2.1.1.重命名文件
以文件py-compile为例,重命名前,该文件存储在子卷v2-client-1对应的节点上,
py-compile文件存储在test2下面即v2-client-1下面: test2 |-- 123 |-- 34 |-- liuhong `-- py-compile |
重命名为py-compile.bak1后,hash后的子卷与更名前子卷相同,该种情况文件仅重命名
-rwxr-xr-x. 1 root root 4142 1?.11 17:34 py-compile.bak1 其扩展属性: >>> xattr.listxattr("/mnt/test2/py-compile.bak1") (u'security.selinux', u'trusted.gfid')//普通文件xattr |
重命名为360buy.com后,在test1,test2即v2-client-0, v2-client-1两个子卷下
test1 |-- 123 |-- 345 |-- 360buy.com //生成了一个文件360buy.com `-- liuhong test1下:---------T. 1 root root 0 1?.16 14:10 360buy.com//文件为空文件 该文件xattr: >>> xattr.listxattr("/mnt/test1/360buy.com") (u'security.selinux', u'trusted.gfid', u'trusted.glusterfs.dht.linkto') >>> xattr.getxattr("/mnt/test1/360buy.com","trusted.glusterfs.dht.linkto") 'v2-client-1\x00' //连接到的地址为子卷v2-client-1 test2 |-- 123 |-- 34 |-- 360buy.com `-- liuhong Test2下:-rwxr-xr-x. 1 root root 4142 1?.11 17:34 360buy.com >>> xattr.listxattr("/mnt/test2/360buy.com") (u'security.selinux', u'trusted.gfid')//扩展属性没变,还是这个键 |
结论:当文件重命名其hash子卷与源子卷不为同一个子卷后,会在hash到的子卷通过mknod创建一个空文件,在xattr中设置了其源子卷为哪个子卷。
再重命名360buy.com为py-compile,会删除子卷v2-client-0上的空文件,在子卷v2-client-1上的文件更名为py-compile。
2.1.2.重命名文件夹
以文件夹liuhong为例,重命名前,存在节点test1,test2,test3,test4:
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。test1,test2,test3省略 test4 |-- 123 `-- liuhong |
添加2个节点test7,test8后,将文件夹liuhong重命名为360buy.com,则
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。test1,test2,test3省略 test4 |-- 123 `-- 360buy.com test7 |-- 123 `-- 360buy.com test8 |-- 123 `-- 360buy.com 重命名虽然在新加的brick上创建了文件夹,这些文件夹没有为期分配hash区间 >>> xattr.listxattr("/mnt/test7")//没有分配hash区间的xattr (u'security.selinux', u'trusted.gfid', u'trusted.glusterfs.test')//.test属性是为了测试文件系统是否支持xattr。 文件hash不能分配到这些子卷上来,当其他子卷填满后文件可分布这些节点上来 执行命令gluster volume rebalance v2 fix-layout start,可以为新创建的文件夹分配hash区间: >>> xattr.listxattr("/mnt/test7/360buy.com") (u'security.selinux', u'trusted.gfid', u'trusted.glusterfs.dht') 解释:在glusterfs hash部分,设计思想是原来分布的文件不会更改其父目录的分布区间,以免添加新的brick后为了配合一致性hash引起以前的文件的移动,所以新创建的父文件夹没有分配hash区间。执行命令fix-layout后,文件夹会重新分配区间
|
结论:文件重命名后,文件夹会分配到当前所有可用子节点上
2.2.负载均衡
利用这个功能可以对已经存在的目录文件进行Rebalance,使得早先创建的老目录可以在新增存储节点上分布,并可对现有文件数据进行迁移实现容量负载均衡。为了便于控制管理,rebalance操作分为两个阶段进行实际执行,即fix layout和migrate data。操作gluster volume rebalance <VOLNAME>{start|stop|status},同时执行以上两个阶段操作,先Fix Layout再Migrate Data
2.2.1 分布区间(FixLayout)
使得早先创建的老目录可以在新增存储节点上分布。为相应目录分配分布区间后,如果之前的文件通过hash算法会会对应到新添加的目录下,则会在新添加目录下建立连接,而真实目录还是位于以前目录下,使用到的命令为fix layout,如:glustervolume rebalance v2 fix-layout start。
举例:dht逻辑卷之前有brick相关目录为/mnt/test1, /mnt/test2, /mnt/test3, …/mnt/test10,如果在该逻辑卷下创建目录360buy.com,在该逻辑卷田间2个brick/mnt/test11,/mnt/test12,现在重命名目录360buy.com为360top.com,则在新添加的brick下会用文件夹360top.com,但是这2个文件夹没有分布区间,文件不能通过hash直接分配到这2个brick。
执行命令:glustervolume rebalance v2 fix-layout start,则
命令运行前: test10 `-- 360top.com |-- account.ring.gz |-- autogen.sh |-- config.h |-- config.sub |-- container.ring.gz |-- COPYING |-- glusterfs.spec |-- Makefile |-- Makefile.am `-- object.builder test11 `-- 360top.com test12 `-- 360top.com 命令运行后: |-- glusterfs.spec.in |-- Makefile |-- Makefile.am |-- object.builder `-- proxy-server.conf test11 `-- 360top.com |-- cert.crt |-- libtool `-- THANKS test12 `-- 360top.com |-- missing `-- NEWS 进入目录test11查看: [root@04:57:06@/mnt/test11/360top.com]#ll ?荤.?.12 ---------T. 1 root root 0 1?.19 16:52 cert.crt ---------T. 1 root root 0 1?.19 16:52 libtool ---------T. 1 root root 0 1?.19 16:52 THANKS 进入目录test12查看: [root@04:57:20@/mnt/test12/360top.com]#ll ?荤.?.8 ---------T. 1 root root 0 1?.19 16:52 missing ---------T. 1 root root 0 1?.19 16:52 NEWS 注 “T”为连接标识,即当dht读取 文件hash到该文件,会重定向到xattr记录的连接brick下读取文件内容 扩展属性内容: >>> xattr.listxattr("missing") (u'security.selinux', u'trusted.gfid', u'trusted.glusterfs.dht.linkto') >>> |