最近一直在试图实现一个文件管理系统,第一个 Demo 在9月初就完成了,从那个 Demo 开始 我经历了3次代码重构,一次文档整理,走到了现在。那么在继续开发的时候,我要先整理下思路,准备材料。 跨平台性
我希望我开发的应用是跨平台的,那么最好就是使用通用的文件传输协议。目前为止,应用都是基于 HTTP 协议的,使用 JSON 传输数据。这样的设计可以保证在在任何平台开发客户端的时候的易用性。也许有人认为HTTP的效率没有直接使用TCP或UDP效率高,但实际上,现在HTTP是互联网上使用最流行的协议,而他屏蔽掉了TCP的细节,提供了标准的报文(这点最重要,自己定义报文我肯定不敢用)是让人很舒心的。

大文件的传输

文件上传是个很常见的需求。尽管HTTP是基于TCP上层的协议,但是HTTP协议本身并不适合处理超大的请求体,文件上传有很大的稳定性问题,如果中途断开了,将前功尽弃。为了改善用户体验或者缓解服务器压力,通常会考虑将文件分成小片,将小片一个个上传,如果中途断开了也能从某个失败的小片开始继续上传。

大文件传输是我解决的第一个问题,总体来说有两台套方案:一套是 WebSocket 方案,一套是 Ajax 方案。就目前来看,似乎 Ajax 方案更胜一筹(主要在易用性方面,而且目前 WebSocket 用的公司不多),但我依旧会持续关注WebSocket方案。截止到写这篇文章,我依然没有决定使用哪套方案。

文件存储

既然文件被分成片上传,那么自然在服务器端需要将分片合并成原始的文件,那么这里存在两种策略:

边传边合并:每上传一个分片,就将分片合并到文件的后面(当前方案)。 传完一起合并:先将分片保存起来,客户端发起一个合并请求时,再将分片合并成一个文件。 文件合并是IO操作,IO操作非常耗时,根据 P_Chou 提示,似乎有一种更好的方案,那就是不合并。下面是抄的:

仔细思考合并这个动作,实际上是将多个文件在文件系统里面复制了一次,而文件的内容并没有任何的变化。如果能够在文件系统层面将分片直接连接起来话,合并文件仅仅是修改一些指针,速度将十分的快。不过文件系统各不相同,能不能实现还需要看。而且,由于笔者使用nfs作为数据存储,nfs的文件读写完全是通过接口提供的,接口也不提供底层的文件系统操作,所以似乎是无法实现。

再思考下去,如果文件系统无法做到将分片直接连接起来的的话,那么从用户接口层(HTTP)是否能做到呢?试想,通过HTTP的方式提供文件的访问,如果HTTP服务器能够知道这个文件是由多个小文件按何种顺序组成的,那么就可以按照顺序将分片依次放在同一个HTTP流中返回,对用户来说一次请求还是得到一个文件,好像文件是合并好的一样,但实际上文件在文件系统并不存在。 这样做需要单独将分片的顺序维护好,每次都要读出分片的顺序和位置,然后依次一个个写入HTTP流中。但是高层的Web编程框架似乎无法支持这种做法。 那么这样做的话,需要实现一个相对底层的文件管理系统,实现文件下载功能。

其实就算是不这样写的,后面也是要实现一个文件下载服务器,因为目前基于 Nginx 的文件下载服务没有很好的用户认证机制。

文件管理与资源管理分离

这个有点拗口。在这一小节我约定文件表示物理设备上存储的真实文件,而资源指的是用户理解的文件,即虚拟文件。

我的想法是把文件管理和资源管理独立成两个单独的服务,文件服务器只负管理那些文件片段的上传、下载删除,资源服务器只负责资源管理(业务逻辑),这样看着比现在的后台清晰很多。资源服务器还是像一个传统的 WEB 应用,但是文件服务器只提供文件服务。

文件服务器我没有准备自己写,因为我发现网上有很多已有的方案,最早是 Google 内部使用的 GFS(闭源), 还有很多支持分布式存储。出名的主要有:

  • TFS (Taobao !FileSystem)
  • HDFS(Hadoop File System)
  • SeaweedFS
  • Lustre
  • MogileFs
  • FastDFS

注:这么多选择,还只是当前列出的,我的内心是崩溃的。

认证机制

可以这样说,目前软件的认证服务、资源服务、文件服务(还有未来的检索服务)都是集成在一起的,但是如果单独出来文件服务的话,那么最好是把这几个服务都分开。

我在开发一个已经失败的项目Marmot的时候曾经使用了Restful思想,采用了 OAuth 2.0认证机制,我现在觉得,在项目变得越来越大时, REST 的价值就能更好的体现。

这种认证机制简单来说就是客户端先向认证服务器申请一个 TOKEN,然后在请求限定资源时要带上TOKEN,服务器再处理请求时需要向认证服务器验证TOKEN的合法性。

总结

最后我用一个图来说明所有设想。

(不好意思把图丢了)