GlusterFS源码阅读

概述

GlusterFS是一个开源的分布式文件系统,具有强大的横向扩展能力,通过扩展能够支持数PB存储容量和处理数千客户端。GlusterFS借助TCP/IP或InfiniBandRDMA网络将物理分布的存储资源聚集在一起,使用单一全局命名空间来管理数据。GlusterFS基于可堆叠的用户空间设计,可为各种不同的数据负载提供优异的性能。

模块化堆栈式架构简介 GlusterFS采用模块化、堆栈式的架构,可通过灵活的配置支持高度定制化的应用环境,比如大文件存储、海量小文件存储、云存储、多传输协议应用等。每个功能以模块形式实现,然后以积木方式进行简单的组合,即可实现复杂的功能。比如,Replicate模块可实现RAID1,Stripe模块可实现RAID0,通过两者的组合可实现RAID10和RAID01,同时获得高性能和高可靠性。

GlusterFS的总体架构如上图所示,它可以分为存储服务器(brick server)和客户端组成。客户端与服务器之间使用网络进行交互,有两种方式可选TCP/IP或者RDMA,默认采用TCP/IP的方式。 存储服务器用来提供基本的数据存储功能,客户端提交的文件数据通过调度策略保存在不同的服务器上。在Brick上运行着glusterfsd进程,负责处理来自客户端的请求。在服务器端,数据可以以多种方式存储(EXT3,EXT4等),服务端不关心最终文件系统的类型。 由于没有元数据服务器,客户端承担了大部分的功能,包括数据卷管理、IO调度,文件定位,数据缓存,文件修复等功能。 GlusterFS文件交互

上图是GlusterFS的客户端和服务器端的交互示意图。 对用户来说,Glusterfs服务器集群是完全透明的,无需关心数据的存储的过程。客户端上运行Glusterfs进程,它实际是Glusterfsd的符号链接,利用FUSE(File system in User Space)模块将GlusterFS挂载到本地文件系统之上,实现POSIX兼容的方式来访问系统数据。 在客户端,通过mount的方式,GlusterFS会挂载到本地的目录下。挂载以后,客户端会自动启动一些进程,用于处理文件的请求。 用户对目录的操作通过VFS传给FUSE来处理。而FUSE最终将文件处理的操作交给GlusterFS client进程来进行。 clinet 进程通过网络,将数据递交给GlusterFS Server,Server最终完成数据的操作。 堆栈式架构

上图描述的是一个使用Replicate功能构建的RAID1文件系统。每个文件保存两份,互为镜像。 GlusterFS是模块化堆栈式的架构设计。GlusterFS的模块称为Translator,借助Translator可以方便的拓展文件系统的功能,开发者也可以基于其建立一套自己的文件读写机制。GlusterFS中的所有功能都通过Tanslator来实现,比如Cluster, Storage, Performance, Protocol, Features等,基本简单的模块可以通过堆栈式的组合来实现复杂的功能。 GlusterFS将这些Translator组成一颗Translator的树,采用分层设计。每个Translator节点将请求处理完以后,交给其子节点处理。每个节点都有其自己的数据结构和临时变量,在交给下一节点处理时,需要保存当前节点的数据结构。所以,GlusterFS设计了一种存储栈来完成该工作。

Translator在GlusterFS内部数据结构中称为xlator_t。

每个xlator_t 结构定义了大量的函数指针, 这些函数指针大致可以分为三类: a) 普通的数据处理函数指针(用于正常的数据处理) b) 回调函数指针,用于处理结果的返回。 c) 管理类函数指针从源代码中可以看到, FUSE对这三类指针的定义为空,因为 FUSE是树根节点,所以没有定义这些处理函数。 交互过程

  1. 当向 /mnt/glusterfs 中写入数据时, linux 会向 VFS 传递这个动作,VFS会将实际的处理交给 FUSE(kernel)文件系统, 然后通过 /dev/fuse 这个设备文件, 将实际的写处理递交给了 glusterfs 系统树的 fuse_xlator_t 这个根节点,这样, 一个写数据流就正式流入了 系统的 xlator_t 结构树。
  2. fuse_xlator_t 这个树的根节点会将这个写处理递交给他的子节点, 具体的递交是通过下面的这个宏完成的:
  3. 这样, 每一个xlator_t 节点都会按照上面的方式将写数据递交给他的子节点来处理, 直到到达了树的叶子节点。
  4. 叶子节点是跟 socket 关联在一起的, 所以这个写操作就通过 socket 递交给glusterfs 服务器处理。
  5. 至于在客户端这边,处理结果的返回,与上面 父节点-> 子节点 的处理方向相反, 是由子节点 -> 父节点,这样, 处理的结果会一直被返回给 fuse_xlators, 然后通过 FUSE(kernel)返回给用户。 主要模块 DHT模块

    该xlator主要实现了文件的哈希分布,将0到2的32次方根据子卷的个数平均划分若干个区间,文件到达DHT时,会根据文件名计算所得的哈希值所在的区间,来决定该文件落在哪个子卷上。其中各个子卷的哈希区间记录在父目录的扩展属性中。此外,该模块还实现了数据迁移和扩容功能。 AFR模块

    该xlator主要实现了文件级别的镜像冗余功能,类似raid1功能,不过不是块级别的。数据到达AFR时,会将ChangeLog加1,然后写数据,待所有子卷全部写成功后,再将ChangeLog减1。若需要修复时,根据ChangeLog判断哪个是source卷。实际的修复流程非常复杂,包括meta,entry等。冗余卷没有主从之分,任何一个子卷都可以保证上层的读写请求,可在不影响上层应用的情况下执行修复功能。 Stripe模块

    该xlator主要实现了文件的写条带,即文件到达Stripe时,会将文件按固定大小的条带写入各个子卷,类似raid0功能。在高版本中,有两种模式:写空洞文件模式和聚合模式。该模块原理和实现都较DHT和AFR模块简单,且代码量较少,在此不再赘述。