在同一台服务器上保存所有项目文件,不同类型的文件存放在不同的子目录下。如下图所示:
在这样的项目文件组织结构中,包含include目录下的头文件,编译source目录下的源文件,链接lib目录下的库文件,在bin目录下生成二进制可执行文件。运行bin目录下的可执行文件,产生一到多个应用程序进程实例,它们会根据用户的需要,从resource目录下获取,诸如图像、音频、视频等,不同类型的多媒体资源。docs目录用于存放与项目有关的各种文档。
实现简单:在一台计算机上,直接利用文件系统划分不同类型的文件;
访问方便:应用程序无需借助任何复杂技术,就能直接访问资源目录下的多媒体数据。
不易管理:源代码、可执行代码、第三方代码和资源媒体,紧密耦合在同一台计算机的同一个文件系统中。文件越多,分类越细,目录结构越复杂,文件存放越混乱;
性能不佳:同时为多个客户机提供服务的多个进程或线程,同时访问同一台计算机同一个文件系统中的资源文件,会导致服务器整体性能下降,延长响应时间,影响用户体验。
随着公司业务的不断发展,将代码和资源放在同一台服务器上的弊端将日益凸显,为此考虑引入独立的资源服务器专门负责存储和管理包括图像、音频、视频等在内的多媒体资源。具体操作过程包括:
通过FTP或SSH将资源文件上传到资源服务器的某个特定目录下;
在资源服务器上布署Ngnix或Apache,可以特定URL访问该目录下的资源文件;
应用程序通过此URL从资源服务器上下载该资源文件,为客户机提供业务服务。
如下图所示:
对图像、音频、视频等多媒体资源的访问,因其涉及到频繁的系统上下文切换和大量的磁盘I/O操作,通常都会十分消耗系统资源。将其分离出来,运行在独立的资源服务器上,势必极大地减轻应用服务器的压力,使之更加专注于发挥面向业务逻辑的动态处理效能;
将有关数据存储的工作,交由独立的资源服务器专门负责,便于对数据进行扩容、容灾以及迁移;
独立的资源服务器可以借助于负载均衡、数据缓存等技术手段,进一步提高对资源的访问效率,同时为日后迁移到CDN做好准备。
只靠一台资源服务器容易形成性能瓶颈,其容灾和垂直扩展性也稍差。
随着业务的持续发展,对应用系统的高响应性和高可用性的要求会越来越高。为了解决单台资源服务器性能有限、容灾和垂直扩展性差等问题,考虑引入分布式存储。分布式存储由以下三部分组成:
仲裁系统:由算法确定文件的存储位置;
存储系统:实际存储文件内容;
容灾系统:相互备份和热切换。
如下图所示:
高响应性:通过在多台存储主机间均衡负载,消除了由单台资源服务器形成的性能瓶颈,缩短了对应用服务器的响应时间;
高可用性:多台存储主机互为备份,任何一台主机出现故障,都不至影响整个系统的平稳运行;
高扩展性:可根据业务需要,灵活增减存储主机的数量,无需中断系统运行。
系统复杂度稍高,需要更多的服务器。
FastDFS是一款开源轻量级分布式文件系统。它解决了大数据量的存储和负载均衡等问题,特别适合以介于4K到500M字节之间的中小文件为载体的在线服务,如相册、音乐、视频网站等。现有的很多基于FastDFS开发的业务系统,正在向用户提供诸如网盘、社区、广告和应用下载等存储服务。
FastDFS采用纯粹C语言实现,支持Linux、FreeBSD等类UNIX操作系统,类似于GFS (Google File System,谷歌文件系统),并非通用文件系统,只能通过专有API访问,目前提供了面向C、Java和PHP的编程接口,专为互联网应用量身定做,解决海量文件存储问题,追求高性能和高扩展性。FastDFS可被视为一种面向文件的键——值对存储系统,称其为分布式文件存储服务更为恰当。
FastDFS具有如下特性:
文件不分块存储,所有上传文件与文件系统中的文件一一对应;
相同内容的文件只保存一份,节约存储空间;
支持HTTP下载,既可使用内置Web Server,亦可与其它Web Server集成;
支持在线扩容;
支持主从文件;
支持文件元数据;
支持高并发访问,整体性能卓越。
完整的FastDFS系统包括跟踪服务器(Tracker)、存储服务器(Storage)和客户机(Client)三个重要角色,以及组(Group)、元数据(Meta Data)等逻辑对象。如下图所示:
跟踪服务器相当于FastDFS的大脑,不论是上传还是下载,都要由跟踪服务器来分配资源;
客户机可借助Ngnix等静态服务器,使用和缓存跟踪服务器上的服务和数据;
存储服务器被划分为若干组或卷,组与组平行,可依资源使用情况增减;
每个组内的多台存储服务器,彼此同步,互为备份,以利容灾。
跟踪服务器的主要任务是调度协调和负载均衡,同时也是客户机与存储服务器之间数据传输的枢纽;
跟踪服务器负责管理所有存储服务器和组。每台存储服务器启动后,都会与跟踪服务器建立连接,向其通告自己所隶属的组,并保持周期性心跳。跟踪服务器据此维护组——存储服务器映射表;
跟踪服务器需要管理的元数据很少,全部由存储服务器汇报产生,仅在内存中,无需持久化;
直接增加跟踪服务器机器即可扩容跟踪服务器集群。集群中的每台跟踪服务器完全对等,同时接收来自存储服务器的周期性心跳,生成元数据,提供读写服务。
存储服务器直接将文件的内容数据和元数据保存在计算机的文件系统中;
整个存储集群以组为单位组织存储。每个组内包含一到多台存储服务器,互为备份。组的存储容量由组内容量最小的存储服务器决定。建议组内配置容量相同的存储服务器,以免造成存储空间的浪费;
每台存储服务器都将文件保存到本地文件系统中,可根据需要配置多个存储目录。比如有十块硬盘,分别挂载到/data/disk1,/data/disk2,...,/data/disk10目录下,可将这十个目录都配置为存储目录;
存储服务器接收到写文件请求时,会根据事先配置好的规则,选择一个存储目录。为了避免单个目录下文件数过多,服务器首次启动时,会在每个存储目录下创建两级子目录,每级256个,共65536个子目录。新文件会以散列的方式被路由到其中某个特定子目录下,保存为本地文件。如下图所示:
xxxxxxxxxx
281/data/
2 |
3 +--disk1/
4 | |
5 | +--00/
6 | | |
7 | | +--00/
8 | | | |
9 | | | +--wKgADl7PZmWAWDR0A3_8qu7Bxj0478.mp4
10 | | | +--wKgADV7PZeSAYq-HA8xf__JA1NA431.mp4
11 | | |
12 | | +--01/
13 | | .
14 | | .
15 | | .
16 | | +--FF/
17 | |
18 | +--01/
19 | .
20 | .
21 | .
22 | +--FF/
23 |
24 +--disk2/
25 .
26 .
27 .
28 +--disk10/
作为业务请求的发起方,客户机通过专有接口,使用TCP/IP协议与跟踪服务器和存储服务器通信;
客户机以库函数的形式,提供访问文件的接口,如:upload、download、append、delete等。
组亦称卷。同组内的存储服务器,内容一致,互为备份且彼此对等;
文件上传、删除等操作可在组内任意一台存储服务器上进行,其它服务器与之同步;
组模式的优点:
应用隔离:将不同应用的数据保存到不同的组中;
负载均衡:将同一种应用的数据分配到不同的组中;
副本数定制:组内存储服务器的数量即为该组的副本数。
组模式的缺点:
组的容量受单机存储容量的限制;
当有机器故障时,数据恢复只能依赖组内的其它机器,恢复时间会很长。
元数据是以键——值对形式表示的文件属性。如:width=1024,height=768等。
完整的文件上传流程如下图所示:
每台存储服务器通过周期性心跳向跟踪服务器定时通告其自身状态,后者负责汇总和维护这些信息;
客户机向跟踪服务器发起有关上传文件的请求;
跟踪服务器在其维护的有关存储服务器的信息中查询可用的存储服务器;
跟踪服务器向发起上传请求的客户机响应以可用存储服务器的IP地址和端口号;
客户机根据接收到的IP地址和端口号向特定存储服务器上传文件的内容数据和元数据;
存储服务器根据接收到的文件数据生成唯一的文件标识;
存储服务器将接收到的文件内容数据保存到文件系统的特定目录下;
存储服务器向客户机返回文件的访问路径;
客户机保存从存储服务器接收到的文件访问路径,以备下载之需。
在文件上传的过程中会涉及到以下内部处理:
选择跟踪服务器
选择组
选择存储服务器
选择存储目录
生成文件标识
生成访问路径
当跟踪服务器集群中不止一台跟踪服务器时,由于各跟踪服务器之间是完全对等的关系,客户机在上传文件时可以任意选择一台跟踪服务器,发起有关上传文件的请求。
当跟踪服务器接收到来自客户机的上传文件请求时,会为该文件分配一个用于存储的组。FastDFS支持如下选组规则:
在各个组之间轮询选择(Round Robin)
选择一个特别指定的组(Specified Group)
选择空闲空间最大的组(Load Balance)
在跟踪服务器为请求上传文件的客户机选定组以后,将在该组内依如下规则为其选择一台存储服务器用于存储文件:
在各个存储服务器之间轮询选择(Round Robin)
按照IP地址的有序序列顺序选择(First Server Ordered By IP)
按照事先配好的优先级顺序选择(First Server Ordered By Priority)
跟踪服务器最终将选定的存储服务器的IP地址和端口号,以响应报文的形式发回给请求上传文件的客户机。后者根据接收到的IP地址和端口号向该存储服务器上传文件的内容数据和元数据。存储服务器需要为接收到的文件内容数据选择一个特定的存储目录用于保存文件。FastDFS支持如下存储目录选择规则:
在多个存储目录之间轮询选择(Round Robin)
选择空闲空间最大的存储目录(Load Balance)
在选定存储目录之后,存储服务器会将其IP地址、文件创建时间、文件大小、文件内容的循环冗余校验码(CRC32)和一个随机数拼接在一起,做Base64编码,得到一个可显示字符串,即文件标识(File ID)。
每个存储目录下有两级子目录,每级256个,共65536个子目录。存储服务器对文件标识做两次散列,路由到其中某个特定子目录下,将文件以文件标识为文件名保存在该子目录下。
当文件被保存到某个子目录后,即可认为该文件已经存储成功,接下来将为该文件生成一个访问路径。文件的访问路径由组、存储目录、两级子目录、文件标识和表示文件类型的后缀组成。如:
Group1/data/disk1/00/00/wKgADl7PZmWAWDR0A3_8qu7Bxj0478.mp4
而后客户机将通过类似下面的URL下载该文件:
http://192.168.0.11:8080/Group1/data/disk1/00/00/wKgADl7PZmWAWDR0A3_8qu7Bxj0478.mp4
http://192.168.0.12:8080/Group1/data/disk1/00/00/wKgADl7PZmWAWDR0A3_8qu7Bxj0478.mp4
http://192.168.0.13:8080/Group1/data/disk1/00/00/wKgADl7PZmWAWDR0A3_8qu7Bxj0478.mp4
http://192.168.0.14:8080/Group1/data/disk1/00/00/wKgADl7PZmWAWDR0A3_8qu7Bxj0478.mp4
其中192.168.0.11、192.168.0.12、192.168.0.13和192.168.0.14分别为Tracker1、Tracker2、Tracker3和Tracker4的IP地址,8080为每台跟踪服务器的Web服务端口。
完整的文件下载流程如下图所示:
每台存储服务器通过周期性心跳向跟踪服务器定时通告其自身状态,后者负责汇总和维护这些信息;
客户机向跟踪服务器发起有关下载文件的请求,其中包括组和文件标识;
跟踪服务器根据组和文件标识中的存储服务器IP地址、文件创建时间、文件大小等信息,查询可用的存储服务器;
跟踪服务器向发起下载请求的客户机响应以可用存储服务器的IP地址和端口号;
客户机根据接收到的IP地址和端口号向特定存储服务器提交文件的存储路径,其中包括文件的存储目录、两级子目录、文件标识和表示文件类型的后缀;
存储服务器根据接收到的文件存储路径,在其文件系统中查找该文件;
存储服务器向客户机返回文件的内容数据;
客户机将从存储服务器接收到的文件内容数据保存在本地,以备后续之需。
由于组内各存储服务器间的文件同步是于后台异步进行的,因此当客户机请求下载某文件时,该文件可能尚未被同步到组内的每一台存储服务器上。为了避免恰好选中某台尚未取得同步的存储服务器,跟踪服务器需依如下规则选择特定组内的可用存储服务器:
从文件标识中提取存储服务器的IP地址,该地址所标识的存储服务器被称为源存储服务器,其中肯定存有被请求下载文件的完整内容。因此源存储服务器一定是可用存储服务器;
从文件标识中提取文件的创建时间,与存储服务器开始同步该文件的时间进行比较:
创建时间等于同步时间:这说明几乎在创建该文件的同时就开始同步了,但并不能保证同步已经完成,因此还要进一步检查当前时间与创建时间的差是否大于最大同步过程时间,即只有经过足够长的时间,才能确定文件同步完了;
xxxxxxxxxx
71创建时间 最大同步过程时间 当前时间
2 | ________________|________________ |
3 |/ \ |
4 +-----------------+-----------------+-----------------+----------------> 时间
5 |\_______________/|
6 | | |
7同步时间 同步过程 同步完成
创建时间小于同步时间:存储服务器上的文件是被顺序同步的,即同步完一个文件再同步下一个文件,因此那些在该文件被同步之前就被同步的文件,此时肯定已经同步完了。
xxxxxxxxxx
71创建时间 已完成同步的文件 该文件 当前时间
2 | ________________|________________ | |
3 |/ \| |
4 +-----------------+-----------------+-----------------+----------------> 时间
5 |\_______________/|\_______________/|\_________________________________
6 | | | | | |
7同步时间 同步过程 同步时间 同步过程 同步时间 同步过程
如果当前时间与从文件标识中提取的文件创建时间之间的间隔足够大,比如大于最大同步延迟时间,那么可以认为该文件已经同步完了。
xxxxxxxxxx
71创建时间 最大同步延迟时间 当前时间
2 | ________________|________________ |
3 |/ \ |
4 +-----------------+-----------------+-----------------+----------------> 时间
5 |\_______________/|
6 | | |
7 同步时间 同步过程 同步完成
在客户机成功上传一个文件后,该文件即被保存在某个组内的某台存储服务器上。运行在该存储服务上的同步线程会按照文件时间戳的先后顺序,将这些文件同步到组内其它存储服务器上:
针对每一台目标存储服务器,都有一个进度值,用最后一个被同步文件的时间戳表示;
同步进度会被记录在文件中,即便服务器宕机,重启后仍能接续之前的进度继续同步。
如图所示,组Group1中的存储服务器Storage1接收到不同客户机上传的文件A、B、C和D,其时间戳分别为1:00、2:00、3:00和4:00。经过一段时间后,文件A被同步到组内另外三台存储服务器Storage2、Storage3和Storage4上;文件B被同步到Storage2和Storage3上;文件C仅被同步到Storage2上;文件D则尚未被同步到任何存储服务器上。这时Storage1对Storage2的同步进度为文件C的时间戳3:00,Storage1对Storage3的同步进度为文件B的时间戳2:00,而Storage1对Storage4的同步进度则为文件A的时间戳1:00。这三个同步进度都保存在Storage1的磁盘文件中。这时若Storage1宕机,待其重启后,将查找时间戳分别晚于这三个同步进度的文件向其对应的存储服务器做同步。
由此可见,若源存储服务器对目标存储服务器的同步进度为T,则说明在源存储服务器上所有时间戳不晚于T的文件都已被同步到目标存储服务器上。
每台存储服务器启动后,都会与跟踪服务器建立连接,并通过周期性心跳向跟踪服务器定时通告其自身状态,其中就包括该存储服务器对其它存储服务器的同步进度。跟踪服务器将根据这些同步进度为每台存储服务器生成一个同步时间戳,所有时间戳不晚于同步时间戳的文件肯定已被同步到该存储服务器上。
xxxxxxxxxx
221 / +-------------+-------------+-------------+-------------+
2 | Storage1 | A | B | C | D |
3 | +-------------+-------------+-------------+-------------+---> t
4 | T1
5 | +-------------+-------------+-------------+-------------+
6 | Storage2 | E | F | G | H |
7 | +-------------+-------------+-------------+-------------+---> t
8 | T2
9 | +-------------+-------------+-------------+-------------+
10 | Storage3 | I | J | K | L |
11 | +-------------+-------------+-------------+-------------+---> t
12Group1 | T3
13 | +-------------+
14 | Storage4 | A |
15 | +-------------+-------------+
16 | | E | F |
17 | +-------------+-------------+-------------+
18 | | I | J | K |
19 | +-------------+-------------+-------------+-------------+
20 | | M | N | O | P |
21 | +-------------+-------------+-------------+-------------+---> t
22 \ 同步时间戳(T1)
如图所示,组Group1中的存储服务器Storage1、Storage2、Storage3和Storage4。其中:
Storage1对Storage4的同步进度为T1,即在Storage1上所有时间戳不晚于T1的文件都已被同步到Storage4上,如文件A;
Storage2对Storage4的同步进度为T2,即在Storage2上所有时间戳不晚于T2的文件都已被同步到Storage4上,如文件E和F;
Storage3对Storage4的同步进度为T3,即在Storage3上所有时间戳不晚于T3的文件都已被同步到Storage4上,如文件I、J和K;
取三个同步进度T1、T2和T3中的最小值T1作为存储服务器Storage4的同步时间戳。在其它存储服务器上如果有某个文件的时间戳不晚于Storage4的同步时间戳(T1),则可以断定该文件此时肯定已经被同步到Storage4上了,如文件A、E和I,而对于时间戳晚于Storage4同步时间戳(T1)的文件,则可能已被同步,如文件F、J和K,也可能尚未同步,如文件B、C、D、G、H和L。使用类似的方法,可以得到每台存储服务器的同步时间戳。
为了下载文件,客户机需要向跟踪服务器提交被下载文件的访问路径,该路径是在文件上传过程中由存储服务器生成并返回给客户机的。文件的访问路径由组、存储目录或虚拟路径、两级子目录、文件标识和表示文件类型的后缀组成。如下图所示:
xxxxxxxxxx
71Group1/data/disk1/00/00/wKgADl7PZmWAWDR0A3_8qu7Bxj0478.mp4
2\____/\__________/\____/\____________________________/\__/
3 | | | | |__ 类型后缀
4 | | | |____________________ 文件标识
5 | | |_____________________________________ 两级子目录
6 | |_______________________________________________ 存储目录
7 |_______________________________________________________ 组
xxxxxxxxxx
71Group1/M00/00/00/wKgADl7PZmWAWDR0A3_8qu7Bxj0478.mp4
2\____/\___/\____/\____________________________/\__/
3 | | | | |_________ 类型后缀
4 | | | |___________________________ 文件标识
5 | | |____________________________________________ 两级子目录
6 | |__________________________________________________ 虚拟路径
7 |_______________________________________________________ 组
注:虚拟路径本质上是个指向存储目录的符号链接。
文件标识是文件名中类似“wKgADl7PZmWAWDR0A3_8qu7Bxj0478”的字符串。事实上它是一串二进制字节序列的Base64编码。该序列由源存储服务器IP地址、文件创建时间、文件大小、文件内容的循环冗余校验码(CRC32)和一个随机数组成。如下图所示:
xxxxxxxxxx
51源存储服务器IP地址 + 文件创建时间 + 文件大小 + 文件内容的循环冗余校验码(CRC32) + 随机数
2\_______________________________________________________________________________/
3 | Base64
4 v
5 wKgADl7PZmWAWDR0A3_8qu7Bxj0478
当客户机需要下载文件时,首先将包含访问路径的下载请求发送给跟踪服务器。访问路径中携带了组和文件标识等信息。跟踪服务器根据组和文件标识中的源存储服务器IP地址、文件创建时间、文件大小等信息,查询可用的存储服务器,并返回给客户机。客户机再次将包含访问路径的下载请求发送给上一步得到的可用存储服务器,后者根据访问路径中的存储目录或虚拟路径、两级子目录、文件标识和类型后缀定位到具体文件,读取其内容,返回给客户机。如下图所示:
达内集团◇C++/嵌入式◇闵卫