GlusterFS场景优化之文件预分配

浏览02019-06-20 22:38:32 2

随着新技术的持续发展,全球数据量将继续高速增长,据分析机构预测,中国数据量增长最为迅速,预计到2025年将增加到48.6ZB。其中视频监控是数据增长的重要推动因素之一,为了支持大规模的视频监控与分析,对于存储系统来说,提出了新的要求和挑战。由于数据规模的不断增大,相比于传统存储而言,分布式存储的地位显得越来越重要。 

问题引入

  GlusterFS是一个开源的分布式文件系统,它是一个自由软件。与其他分布式文件系统(如Ceph、HDFS、FastDFS等)相比,GlusterFS具有独特的设计,例如无元数据中心、用户态堆栈式设计、全局统一命名空间等,具有强大的横向扩展能力、良好的可靠性和出色的性能,可广泛应用于各类非结构化数据的存储与共享环境,例如流媒体、数据分析以及其他数据和带宽密集型业务。

  然而,在实际环境测试时,我们发现开源的GlusterFS在某种视频监控应用场景下表现的并不是十分出色,当然GlusterFS的这种表现是和具体应用相关的。测试的场景如下:

图1.jpg

  业务系统的多个应用服务器访问同一个GlusterFS存储集群,每个应用服务器上运行一套应用软件,应用软件与GlusterFS存储相关的模块主要有两个,一个负责写(单线程),另一个负责读(单线程)。每个应用服务器写入存储的文件数量与其连接的摄像头数量相关,具体是一个摄像头对应两个文件,一个视频数据文件,一个帧文件,数据文件大小为1GB左右,帧文件为几KB,例如一台应用服务器连接100个摄像头,则写线程要处理200个文件。每30分钟,会有一台或几台应用进行文件切换动作,所谓文件切换就是关闭其正在写入的所有文件,然后打开同样数量的新文件,再继续进行数据写入。文件切换动作只由写线程处理,在此期间,没有进行文件切换的应用服务器仍然向存储写入数据。
  使用开源GlusterFS构建一个基于EC的GlusterFS文件系统,采用POSIX访问协议,在上述场景下测试遇到如下情况:向存储中写入和读取数据性能良好,只是应用软件在进行文件切换动作时的时延较高(相对于应用软件要求标准),从而导致应用软件的自身任务(写请求)积压较多,给用户的体验不好,并且存在丢失数据的风险。

问题分析与方案提出

  对于存储系统性能而言,一般可以通过throughput、IOPS和latency三个指标来衡量。对于上述测试情况,GlusterFS文件系统的读写性能可以较好的满足应用程序,但是在关闭和创建文件这些元数据操作上时延较高,即主要体现在latency指标方面。

  在经过充分测试和仔细分析后,我们发现其影响因素是两方面的,一方面应用软件使用单线程执行文件切换操作,并且关闭旧文件与打开新文件是顺序执行的,并发度较低,这是应用软件整体时延较大的原因之一。

图2.jpg

另一方面,对于GlusterFS来说,有如下几方面影响因素:
(1)无元数据中心架构,导致不擅长处理创建和关闭文件等元数据操作。
(2)Fuse-bridge模块处理文件查找的机制,会触发多次的GlusterFS内部通信。
(3)EC卷模式,客户端应用的一个文件请求通过一个EC模块会被分为多个请求(具体与配置的冗余度相关),分别发送到一个EC子卷对应的每个brick服务进程,并在所有应答都返回到EC模进行聚合后,才会返回应答给客户端应用,相比没有冗余的哈希卷增大了应用访问时延。
(4)GlusterFS内部的锁机制,io-threads、EC、RPC通信等模块中存在一定程度的锁竞争情况。
(5)基于POSIX协议的数据访问路径较长,对于创建文件请求没有缓存可用,需要真正发送到存储节点brick上,并在处理完成后返回。
(6)GlusterFS的posix模块处理创建文件请求时的额外操作较多,例如多次stat调用、设置扩展属性、设置属主属组、设置acl、创建硬链接、可能还需要创建二级目录等。
  总结一下,以上两方面共同作用,导致应用程序文件切换的时延较高,然而从应用程序方面优化往往是不现实的,因此,需要从GlusterFS存储方面寻找解决方案。

图3.jpg

  针对上述应用场景和影响因素分析,在应用软件不做调整的情况下,为了满足应用软件的时延要求,基于目前GlusterFS的系统架构和工作原理,综合考虑优化工作的有效性和复杂性,我们提出了GlusterFS文件预分配方案。

  文件预分配方案的思路是在每个GlusterFS客户端程序内部维护一定数量的预先创建的文件,当应用程序打开新文件时,使用预先创建的文件信息应答应用程序,并在GlusterFS客户端程序内部记录实际文件与预先创建的文件之间的对应关系,这样以来应用打开新文件(时延主要因素)不受GlusterFS内部锁竞争、数据访问路径较长、通信网络状况和存储端磁盘繁忙状况这些因素的影响,时延会非常低,从而大大降低了应用进行文件切换动作的总时延。

文件预分配方案架构

  首先看下原生GlusterFS的总体(外部)架构和内部架构,然后给出引入文件预分配后的内部架构(外部架构与原生GlusterFS相同),通过两者比较,更加有助于理解GlusterFS文件预分配方案。

图4.jpg

  GlusterFS总体架构如图4所示,主要由存储服务器(GlusterFS Brick)、客户端(GlusterFS Client)和存储网关(NFS/Samba以及API)构成。该架构中没有元数据服务组件,采用完全对等的设计,对其强大扩展能力和出色的性能有重要意义。

  • 存储服务器

    存储服务器主要提供基本的数据存储功能,其上运行的glusterfsd进程负责处理来自其他组件的数据请求。最终的文件数据通过统一的调度策略分布在不同的存储服务器上,数据以原始格式直接存储在服务器的本地文件系统上,如EXT4、XFS和ZFS等。

  •  客户端
    由于没有元数据服务器,GlusterFS将更多的功能放到了客户端,包括数据卷管理、I/O调度、文件定位、数据缓存等功能。客户端上运行glusterfs进程,它实际是glusterfsd的符号链接,只是运行的同一程序的不同代码逻辑,利用FUSE模块将GlusterFS文件系统挂载到本地文件系统之上,实现POSIX兼容的方式来访问存储服务器上的数据。

  • 存储网关
    即NAS网关,利用GlusterFS提供的API访问存储服务器。对于没有运行GlusterFS原生客户端的客户应用,可以基于NFS/CIFS标准协议,通过存储网关访问存储服务器上数据。

图5.jpg  

如图5所示,GlusterFS内部软件架构是模块化堆栈式设计。模块称为translator,是GlusterFS提供的一种强大机制,借助这种良好定义的接口可以高效简便地扩展文件系统的功能。每个translator都是一个动态库,运行时根据配置动态加载。每个模块实现特定基本功能,GlusterFS中所有的功能都是通过translator实现,比如I/O Cache、read ahead、write behind、distribute(DHT)、disperse(EC)、client、server和posix等。

  客户端和存储服务器均有自己的存储软件栈(由一些translator组成),整体上构成了一棵translator功能树。模块化和堆栈式的架构设计,极大降低了系统设计复杂性,简化了系统的实现、升级以及系统维护。


图6.jpg

  基于上述对GlusterFS总体架构和内部软件架构的介绍,再来看引入文件预分配后的GlusterFS内部软件架构(如图6),则比较容易理解其实现原理,主要是增加了一个预分配(pre-allocation)模块。

  文件预分配功能主要是通过在GlusterFS原生客户端软件堆栈中新增加一个预分配模块来实现。具体位置在meta模块(位于客户端堆栈的顶端,是fuse-bridge模块之下的第一个模块,客户端初始化时自动加载)之下,客户端挂载时可以选择加载与否。在预分配模块中,通过预先创建文件并维护其相关信息,以及维护与应用实际要创建的文件之间的对应关系,来加速应用程序创建文件,从而降低其文件切换时延。

实现剖析

  首先概括介绍文件预分配实现总体实现,然后介绍引入文件预分配后的数据访问路径,其次说明几个主要场景的处理过程,最后进行简要总结。

  •  预分配模块组成部分

  GlusterFS文件预分配功能主要由预分配模块实现,预分配模块位于GlusterFS客户端堆栈。该模块遵循GlusterFS模块的实现模板,主要实现了lookup、create、open、writev、flush、fstat、fsetattr、fsetxattr、fgetxattr、fm_setxattr、fsync等接口,同时创建了两个线程,create线程和rename线程,分别用于实现文件预先创建和文件重命名功能。
  预分配模块的内部维护3个双向链表,new链表、inuse链表和purge链表,依次保存预先创建且未被使用的文件信息、预先创建且已使用(与应用实际文件存在对应关系)的文件信息和已使用并且数据写入完成的文件信息(需要重命名的文件)。另外,预分配模块在客户端创建一个数据库,用于持久化保存预分配的文件信息和文件映射关系信息,也便于异常情况的处理。如图7所示,凸显了预分配模块的主要组成部分。

图7.1.jpg


  •  文件预分配数据访问路径

图7.2.jpg

一次完整的数据访问流程如下:
1)    应用程序发出文件请求到客户端的VFS层。
2)    客户端的VFS层通过FUSE转发请求到GlusterFS客户端进程。
3)    GlusterFS客户端进程收到请求后,从fuse-bridge模块开始,依次经过客户端堆栈所有已经加载的模块(包括预分配模块),如果预分配模块检测到当前是创建文件请求,则在处理请求后直接返回应答,不再向下层转发请求;否则最后经过client模块,然后通过网络通信发送到存储服务端。
4)    GlusterFS服务端进程收到请求后,从server模块开始,依次经过服务端堆栈所有已经加载的模块,最后到posix模块,然后通过C库函数或系统调用发送到服务端VFS层。
5)    服务端VFS层收到请求后,发送给具体底层文件系统进行处理,处理之后,应答信息按照原路径返回到应用程序,至此一次客户端请求结束。


  • 客户端进程初始化场景

图7.3.jpg

1)    客户端使用mount命令挂载GlusterFS存储。
2)    GlusterFS客户端进程启动并加载所有模块,包括预分配模块,并与GlusterFS存储端进程交互,完成初始化工作。
3)    预分配模块初始化时创建create线程和rename线程,在init函数中初始化本地数据库。在等待客户端进程初始化完成后(fuse_first_lookup函数返回),create线程创建一定数量的文件,并进入睡眠状态,等待唤醒。在客户端进程初始化完成后,rename线程同样也进入睡眠状态,等待唤醒。
4)    客户端进程准备就绪,等待处理应用程序请求。


  • 文件预创建场景

图7.4.jpg

本方案有两个时机会触发create线程进行文件预创建,一个是在客户端进程初始化时,另一个是当预先创建的文件已经使用了阈值的70%时。一般处理流程如下:
1)    Create线程构造文件创建的请求,通过GlusterFS的同步创建文件接口(syncop_create)向预分配模块的下一层发送请求。
2)    该文件创建请求传输到GlusterFS服务端,并得到处理后返回应答。
3)    Create线程收到创建文件应答,然后在内存中记录下该文件相关信息。
4)    将新创建的文件信息保存到到new链表,同时保存到数据库进行持久化。
5)    重复执行步骤1)到4),直到创建的文件达到阈值,然后create线程进入睡眠状态并等待唤醒。


  • 处理应用请求场景

图7.5.jpg

1)    客户端进程在fuse-bridge模块接收到应用请求,对其进行解析,并发往下一层。
2)    在预分配模块接收到上层请求,根据具体请求类型,进行相关处理,或者直接发往下一层;当需要具体处理时(如create请求),则从new链表中找到一个预分配文件,将其信息返回给应用程序,同时记录并持久化该预分配文件与实际文件的对应关系,并将该预分配文件从new链表转移到inuse链表。如果预创建的文件达到阈值的70%,则需要通知create线程进行相应的处理。
3)    create线程收到通知后,执行具体操作(create),操作完成后继续进入睡眠,等待下次唤醒。
4)    客户端进程在fuse-bridge模块接收到服务端或者预分配模块的应答后,将请求的执行结果返回给应用程序。
5)    客户端进程继续等待处理应用程序请求。


  • 文件重命名场景

图7.6.jpg

1)    预分配模块接收到上层create请求,计算相邻两次create请求之间的时间间隔,如果间隔时间超过重命名时间间隔阈值,则将所有已经使用的预先创建文件移动到purge链表,并通知rename线程进行处理。
2)    Rename线程遍历purge链表,将其中的每一个预分配的文件进行重命名。
3)    对每一个文件重命名后,rename线程同时将其在数据库中的记录删掉。
4)    处理完链表中的所有文件后,rename线程进入睡眠状态并等待唤醒。


  • 客户端进程退出场景

图7.7.jpg

1)    客户端卸载(umount)GlusterFS存储。
2)    客户端进程进入退出流程处理,从fuse-bridge模块开始到client模块,依次调用本层的退出清理函数,释放申请的资源,其中预分配模块负责通知create线程和rename线程退出。
3)    重命名线程关闭与数据库的连接,释放数据库相关资源。
4)    所有客户端模块处理完后,客户端进程调用exit退出进程。

方案验证

使用视频监控模拟程序进行测试,在同等环境下测试多次,开源GlusterFS和本文方案实际测试300个文件切换的时延对比结果如下:

表1.jpg

小结

  本文介绍的GlusterFS文件预分配方案,主要为了解决特定视频监控场景下的文件切换时延问题。从理论分析和实际测试结果上看,效果还是比较显著的。如果读者遇到类似的问题,不妨考虑一下本文的方案。
  文件预分配方案能够大大降低本文所述场景下的文件切换时延,但也存在一定的要求和限制,要求是需要特别熟悉应用程序的访问模式,而限制则是一个客户端正在写入文件时,在另一个客户端访问不到该文件,直到该文件写入完成并被重命名后才能访问到,这也是下一步的优化方向。


发表评论

全部评论(0)